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