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