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