1 /*
2    Copyright (c) 2003, 2010, 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 #include <ndb_global.h>
27 
28 #include <ndbd_exit_codes.h>
29 #include "ErrorReporter.hpp"
30 
31 #include <FastScheduler.hpp>
32 #include <DebuggerNames.hpp>
33 #include <NdbHost.h>
34 #include <NdbConfig.h>
35 #include <Configuration.hpp>
36 #include "EventLogger.hpp"
37 extern EventLogger * g_eventLogger;
38 
39 #include "TimeModule.hpp"
40 
41 #include <NdbAutoPtr.hpp>
42 
43 #define MESSAGE_LENGTH 500
44 
45 static int WriteMessage(int thrdMessageID,
46 			const char* thrdProblemData,
47 			const char* thrdObjRef,
48                         NdbShutdownType & nst);
49 
50 static void dumpJam(FILE* jamStream,
51 		    Uint32 thrdTheEmulatedJamIndex,
52 		    const Uint32 thrdTheEmulatedJam[],
53                     Uint32 aBlockNumber);
54 
55 
56 const char*
formatTimeStampString()57 ErrorReporter::formatTimeStampString(){
58   TimeModule DateTime;          /* To create "theDateTimeString" */
59 
60   static char theDateTimeString[39];
61   /* Used to store the generated timestamp */
62   /* ex: "Wednesday 18 September 2000 - 18:54:37" */
63 
64   DateTime.setTimeStamp();
65 
66   BaseString::snprintf(theDateTimeString, 39, "%s %d %s %d - %s:%s:%s",
67 	   DateTime.getDayName(), DateTime.getDayOfMonth(),
68 	   DateTime.getMonthName(), DateTime.getYear(), DateTime.getHour(),
69 	   DateTime.getMinute(), DateTime.getSecond());
70 
71   return (const char *)&theDateTimeString;
72 }
73 
74 int
get_trace_no()75 ErrorReporter::get_trace_no(){
76 
77   FILE *stream;
78   unsigned int traceFileNo;
79 
80   char *file_name= NdbConfig_NextTraceFileName(globalData.ownId);
81   NdbAutoPtr<char> tmp_aptr(file_name);
82 
83   /*
84    * Read last number from tracefile
85    */
86   stream = fopen(file_name, "r+");
87   if (stream == NULL){
88     traceFileNo = 1;
89   } else {
90     char buf[255];
91     fgets(buf, 255, stream);
92     const int scan = sscanf(buf, "%u", &traceFileNo);
93     if(scan != 1){
94       traceFileNo = 1;
95     }
96     fclose(stream);
97     traceFileNo++;
98   }
99 
100   /**
101    * Wrap tracefile no
102    */
103   Uint32 tmp = globalEmulatorData.theConfiguration->maxNoOfErrorLogs();
104   if (traceFileNo > tmp ) {
105     traceFileNo = 1;
106   }
107 
108   /**
109    *  Save new number to the file
110    */
111   stream = fopen(file_name, "w");
112   if(stream != NULL){
113     fprintf(stream, "%u", traceFileNo);
114     fclose(stream);
115   }
116 
117   return traceFileNo;
118 }
119 
120 // Using my_progname without including all of mysys
121 extern "C" const char* my_progname;
122 
123 void
formatMessage(int thr_no,Uint32 num_threads,int faultID,const char * problemData,const char * objRef,const char * theNameOfTheTraceFile,char * messptr)124 ErrorReporter::formatMessage(int thr_no,
125                              Uint32 num_threads, int faultID,
126 			     const char* problemData,
127 			     const char* objRef,
128 			     const char* theNameOfTheTraceFile,
129 			     char* messptr){
130   int processId;
131   ndbd_exit_classification cl;
132   ndbd_exit_status st;
133   const char *exit_msg = ndbd_exit_message(faultID, &cl);
134   const char *exit_cl_msg = ndbd_exit_classification_message(cl, &st);
135   const char *exit_st_msg = ndbd_exit_status_message(st);
136   int sofar;
137 
138   processId = NdbHost_GetProcessId();
139   char thrbuf[100] = "";
140   if (thr_no >= 0)
141   {
142     BaseString::snprintf(thrbuf, sizeof(thrbuf), " thr: %u", thr_no);
143   }
144 
145   BaseString::snprintf(messptr, MESSAGE_LENGTH,
146                        "Time: %s\n"
147                        "Status: %s\n"
148                        "Message: %s (%s)\n"
149                        "Error: %d\n"
150                        "Error data: %s\n"
151                        "Error object: %s\n"
152                        "Program: %s\n"
153                        "Pid: %d%s\n"
154                        "Version: %s\n"
155                        "Trace: %s",
156                        formatTimeStampString() ,
157                        exit_st_msg,
158                        exit_msg, exit_cl_msg,
159                        faultID,
160                        (problemData == NULL) ? "" : problemData,
161                        objRef,
162                        my_progname,
163                        processId,
164                        thrbuf,
165                        NDB_VERSION_STRING,
166                        theNameOfTheTraceFile ?
167                        theNameOfTheTraceFile : "<no tracefile>");
168 
169   if (theNameOfTheTraceFile)
170   {
171     for (Uint32 i = 1 ; i < num_threads; i++)
172     {
173       sofar = strlen(messptr);
174       if(sofar < MESSAGE_LENGTH)
175       {
176 	BaseString::snprintf(messptr + sofar, MESSAGE_LENGTH - sofar,
177 			     " %s_t%u", theNameOfTheTraceFile, i);
178       }
179     }
180   }
181 
182   sofar = strlen(messptr);
183   if(sofar < MESSAGE_LENGTH)
184   {
185     BaseString::snprintf(messptr + sofar, MESSAGE_LENGTH - sofar,
186                          "\n"
187                          "***EOM***\n");
188   }
189 
190   // Add trailing blanks to get a fixed length of the message
191   while (strlen(messptr) <= MESSAGE_LENGTH-3){
192     strcat(messptr, " ");
193   }
194 
195   messptr[MESSAGE_LENGTH -2]='\n';
196   messptr[MESSAGE_LENGTH -1]=0;
197 
198   return;
199 }
200 
201 NdbShutdownType ErrorReporter::s_errorHandlerShutdownType = NST_ErrorHandler;
202 
203 void
handleAssert(const char * message,const char * file,int line,int ec)204 ErrorReporter::handleAssert(const char* message, const char* file, int line, int ec)
205 {
206   char refMessage[100];
207   Uint32 jamBlockNumber;
208 
209 #ifdef NO_EMULATED_JAM
210   BaseString::snprintf(refMessage, 100, "file: %s lineNo: %d",
211 	   file, line);
212   jam = NULL;
213   jamIndex = 0;
214   jamBlockNumber = 0;
215 #else
216   const EmulatedJamBuffer *jamBuffer =
217     (EmulatedJamBuffer *)NdbThread_GetTlsKey(NDB_THREAD_TLS_JAM);
218   jamBlockNumber = jamBuffer->theEmulatedJamBlockNumber;
219   const char *blockName = getBlockName(jamBlockNumber);
220 
221   BaseString::snprintf(refMessage, 100, "%s line: %d (block: %s)",
222 	   file, line, blockName);
223 #endif
224   NdbShutdownType nst = s_errorHandlerShutdownType;
225   WriteMessage(ec, message, refMessage, nst);
226 
227   NdbShutdown(ec, nst);
228   exit(1);                                      // Deadcode
229 }
230 
231 void
handleError(int messageID,const char * problemData,const char * objRef,NdbShutdownType nst)232 ErrorReporter::handleError(int messageID,
233 			   const char* problemData,
234 			   const char* objRef,
235 			   NdbShutdownType nst)
236 {
237   if(messageID == NDBD_EXIT_ERROR_INSERT)
238   {
239     nst = NST_ErrorInsert;
240   }
241   else
242   {
243     if (nst == NST_ErrorHandler)
244       nst = s_errorHandlerShutdownType;
245   }
246 
247   WriteMessage(messageID, problemData, objRef, nst);
248 
249   g_eventLogger->info("%s", problemData);
250   g_eventLogger->info("%s", objRef);
251 
252   NdbShutdown(messageID, nst);
253   exit(1); // kill warning
254 }
255 
256 int
WriteMessage(int thrdMessageID,const char * thrdProblemData,const char * thrdObjRef,NdbShutdownType & nst)257 WriteMessage(int thrdMessageID,
258 	     const char* thrdProblemData,
259              const char* thrdObjRef,
260              NdbShutdownType & nst){
261   FILE *stream;
262   unsigned offset;
263   unsigned long maxOffset;  // Maximum size of file.
264   char theMessage[MESSAGE_LENGTH];
265   Uint32 thrdTheEmulatedJamIndex;
266   const Uint32 *thrdTheEmulatedJam;
267   Uint32 jamBlockNumber;
268 
269   Uint32 threadCount = globalScheduler.traceDumpGetNumThreads();
270   int thr_no = globalScheduler.traceDumpGetCurrentThread();
271 
272   /**
273    * Format trace file name
274    */
275   char *theTraceFileName= 0;
276   if (globalData.ownId > 0)
277     theTraceFileName= NdbConfig_TraceFileName(globalData.ownId,
278 					      ErrorReporter::get_trace_no());
279   NdbAutoPtr<char> tmp_aptr1(theTraceFileName);
280 
281   // The first 69 bytes is info about the current offset
282   Uint32 noMsg = globalEmulatorData.theConfiguration->maxNoOfErrorLogs();
283 
284   maxOffset = (69 + (noMsg * MESSAGE_LENGTH));
285 
286   char *theErrorFileName= (char *)NdbConfig_ErrorFileName(globalData.ownId);
287   NdbAutoPtr<char> tmp_aptr2(theErrorFileName);
288 
289   stream = fopen(theErrorFileName, "r+");
290   if (stream == NULL) { /* If the file could not be opened. */
291 
292     // Create a new file, and skip the first 69 bytes,
293     // which are info about the current offset
294     stream = fopen(theErrorFileName, "w");
295     if(stream == NULL)
296     {
297       fprintf(stderr,"Unable to open error log file: %s\n", theErrorFileName);
298       return -1;
299     }
300     fprintf(stream, "%s%u%s", "Current byte-offset of file-pointer is: ", 69,
301 	    "                        \n\n\n");
302 
303     // ...and write the error-message...
304     ErrorReporter::formatMessage(thr_no,
305                                  threadCount, thrdMessageID,
306 				 thrdProblemData, thrdObjRef,
307 				 theTraceFileName, theMessage);
308     fprintf(stream, "%s", theMessage);
309     fflush(stream);
310 
311     /* ...and finally, at the beginning of the file,
312        store the position where to
313        start writing the next message. */
314     offset = ftell(stream);
315     // If we have not reached the maximum number of messages...
316     if (offset <= (maxOffset - MESSAGE_LENGTH)){
317       fseek(stream, 40, SEEK_SET);
318       // ...set the current offset...
319       fprintf(stream,"%d", offset);
320     } else {
321       fseek(stream, 40, SEEK_SET);
322       // ...otherwise, start over from the beginning.
323       fprintf(stream, "%u%s", 69, "             ");
324     }
325   } else {
326     // Go to the latest position in the file...
327     fseek(stream, 40, SEEK_SET);
328     fscanf(stream, "%u", &offset);
329     fseek(stream, offset, SEEK_SET);
330 
331     // ...and write the error-message there...
332     ErrorReporter::formatMessage(thr_no,
333                                  threadCount, thrdMessageID,
334 				 thrdProblemData, thrdObjRef,
335 				 theTraceFileName, theMessage);
336     fprintf(stream, "%s", theMessage);
337     fflush(stream);
338 
339     /* ...and finally, at the beginning of the file,
340        store the position where to
341        start writing the next message. */
342     offset = ftell(stream);
343 
344     // If we have not reached the maximum number of messages...
345     if (offset <= (maxOffset - MESSAGE_LENGTH)){
346       fseek(stream, 40, SEEK_SET);
347       // ...set the current offset...
348       fprintf(stream,"%d", offset);
349     } else {
350       fseek(stream, 40, SEEK_SET);
351       // ...otherwise, start over from the beginning.
352       fprintf(stream, "%u%s", 69, "             ");
353     }
354   }
355   fflush(stream);
356   fclose(stream);
357 
358   if (theTraceFileName) {
359     /* Attempt to stop all processing to be able to dump a consistent state. */
360     globalScheduler.traceDumpPrepare(nst);
361 
362     char *traceFileEnd = theTraceFileName + strlen(theTraceFileName);
363     for (Uint32 i = 0; i < threadCount; i++)
364     {
365       // Open the tracefile...
366       if (i > 0)
367         sprintf(traceFileEnd, "_t%u", i);
368       FILE *jamStream = fopen(theTraceFileName, "w");
369 
370       //  ...and "dump the jam" there.
371       bool ok = globalScheduler.traceDumpGetJam(i, jamBlockNumber,
372                                                 thrdTheEmulatedJam,
373                                                 thrdTheEmulatedJamIndex);
374       if(ok && thrdTheEmulatedJam != 0)
375       {
376         dumpJam(jamStream, thrdTheEmulatedJamIndex,
377                 thrdTheEmulatedJam, jamBlockNumber);
378       }
379 
380       globalScheduler.dumpSignalMemory(i, jamStream);
381 
382       fclose(jamStream);
383     }
384   }
385 
386   return 0;
387 }
388 
389 void
dumpJam(FILE * jamStream,Uint32 thrdTheEmulatedJamIndex,const Uint32 thrdTheEmulatedJam[],Uint32 aBlockNumber)390 dumpJam(FILE *jamStream,
391 	Uint32 thrdTheEmulatedJamIndex,
392 	const Uint32 thrdTheEmulatedJam[],
393         Uint32 aBlockNumber) {
394 #ifndef NO_EMULATED_JAM
395   // print header
396   const int maxaddr = 8;
397   fprintf(jamStream, "JAM CONTENTS up->down left->right ?=not block entry\n");
398   fprintf(jamStream, "%-7s ", "BLOCK");
399   for (int i = 0; i < maxaddr; i++)
400     fprintf(jamStream, "%-6s ", "ADDR");
401   fprintf(jamStream, "\n");
402 
403   const int first = thrdTheEmulatedJamIndex;	// oldest
404   int cnt, idx;
405 
406   // look for first block entry
407   for (cnt = 0, idx = first; cnt < EMULATED_JAM_SIZE; cnt++, idx++) {
408     if (idx >= EMULATED_JAM_SIZE)
409       idx = 0;
410     const Uint32 aJamEntry = thrdTheEmulatedJam[idx];
411     if (aJamEntry > (1 << 20))
412       break;
413   }
414 
415   // 1. if first entry is a block entry, it is printed in the main loop
416   // 2. else if any block entry exists, the jam starts in an unknown block
417   // 3. else if no block entry exists, the block is theEmulatedJamBlockNumber
418   // a "?" indicates first addr is not a block entry
419   if (cnt == 0)
420     ;
421   else if (cnt < EMULATED_JAM_SIZE)
422     fprintf(jamStream, "%-7s?", "");
423   else {
424     const char *aBlockName = getBlockName(aBlockNumber);
425     if (aBlockName != 0)
426       fprintf(jamStream, "%-7s?", aBlockName);
427     else
428       fprintf(jamStream, "0x%-5X?", aBlockNumber);
429   }
430 
431   // loop over all entries
432   int cntaddr = 0;
433   for (cnt = 0, idx = first; cnt < EMULATED_JAM_SIZE; cnt++, idx++) {
434     globalData.incrementWatchDogCounter(4);	// watchdog not to kill us ?
435     if (idx >= EMULATED_JAM_SIZE)
436       idx = 0;
437     const Uint32 aJamEntry = thrdTheEmulatedJam[idx];
438     if (aJamEntry > (1 << 20)) {
439       const Uint32 aBlockNumber = aJamEntry >> 20;
440       const char *aBlockName = getBlockName(aBlockNumber);
441       if (cnt > 0)
442 	  fprintf(jamStream, "\n");
443       if (aBlockName != 0)
444 	fprintf(jamStream, "%-7s ", aBlockName);
445       else
446 	fprintf(jamStream, "0x%-5X ", aBlockNumber);
447       cntaddr = 0;
448     }
449     if (cntaddr == maxaddr) {
450       fprintf(jamStream, "\n%-7s ", "");
451       cntaddr = 0;
452     }
453     fprintf(jamStream, "%06u ", aJamEntry & 0xFFFFF);
454     cntaddr++;
455   }
456   fprintf(jamStream, "\n");
457   fflush(jamStream);
458 #endif // ifndef NO_EMULATED_JAM
459 }
460