ErrorReporter.cpp 11.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* Copyright (C) 2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


18 19
#include <ndb_global.h>

20
#include <ndbd_exit_codes.h>
21 22 23 24 25 26 27 28
#include "ErrorReporter.hpp"

#include <FastScheduler.hpp>
#include <DebuggerNames.hpp>
#include <NdbHost.h>
#include <NdbConfig.h>
#include <Configuration.hpp>

29 30
#include <NdbAutoPtr.hpp>

31
#define MESSAGE_LENGTH 500
32

33
static int WriteMessage(int thrdMessageID,
34 35
			const char* thrdProblemData, 
			const char* thrdObjRef,
autotest@mc01.ndb.mysql.com's avatar
autotest@mc01.ndb.mysql.com committed
36 37
			Uint32 thrdTheEmulatedJamIndex,
			Uint8 thrdTheEmulatedJam[]);
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

static void dumpJam(FILE* jamStream, 
		    Uint32 thrdTheEmulatedJamIndex, 
		    Uint8 thrdTheEmulatedJam[]);


const char*
ErrorReporter::formatTimeStampString(){
  TimeModule DateTime;          /* To create "theDateTimeString" */
  
  static char theDateTimeString[39]; 
  /* Used to store the generated timestamp */
  /* ex: "Wednesday 18 September 2000 - 18:54:37" */

  DateTime.setTimeStamp();
  
54
  BaseString::snprintf(theDateTimeString, 39, "%s %d %s %d - %s:%s:%s", 
55 56 57 58 59 60 61
	   DateTime.getDayName(), DateTime.getDayOfMonth(),
	   DateTime.getMonthName(), DateTime.getYear(), DateTime.getHour(),
	   DateTime.getMinute(), DateTime.getSecond());
  
  return (const char *)&theDateTimeString;
}

62 63
int
ErrorReporter::get_trace_no(){
64 65 66
  
  FILE *stream;
  unsigned int traceFileNo;
67 68 69
  
  char *file_name= NdbConfig_NextTraceFileName(globalData.ownId);
  NdbAutoPtr<char> tmp_aptr(file_name);
70 71 72 73

  /* 
   * Read last number from tracefile
   */  
74
  stream = fopen(file_name, "r+");
75 76 77
  if (stream == NULL){
    traceFileNo = 1;
  } else {
78
    char buf[255];
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
    fgets(buf, 255, stream);
    const int scan = sscanf(buf, "%u", &traceFileNo);
    if(scan != 1){
      traceFileNo = 1;
    }
    fclose(stream);
    traceFileNo++;
  }

  /**
   * Wrap tracefile no 
   */
  Uint32 tmp = globalEmulatorData.theConfiguration->maxNoOfErrorLogs();
  if (traceFileNo > tmp ) {
    traceFileNo = 1;
  }

  /**
   *  Save new number to the file
   */
99
  stream = fopen(file_name, "w");
100 101 102 103
  if(stream != NULL){
    fprintf(stream, "%u", traceFileNo);
    fclose(stream);
  }
104 105

  return traceFileNo;
106 107 108 109
}


void
110
ErrorReporter::formatMessage(int faultID,
111 112 113 114 115
			     const char* problemData, 
			     const char* objRef,
			     const char* theNameOfTheTraceFile,
			     char* messptr){
  int processId;
116 117 118 119 120 121
  ndbd_exit_classification cl;
  ndbd_exit_status st;
  const char *exit_msg = ndbd_exit_message(faultID, &cl);
  const char *exit_cl_msg = ndbd_exit_classification_message(cl, &st);
  const char *exit_st_msg = ndbd_exit_status_message(st);

122 123
  processId = NdbHost_GetProcessId();
  
124
  BaseString::snprintf(messptr, MESSAGE_LENGTH,
125 126 127 128 129 130 131 132 133 134 135
	   "Time: %s\n"
           "Status: %s\n"
	   "Message: %s (%s)\n"
	   "Error: %d\n"
           "Error data: %s\n"
	   "Error object: %s\n"
           "Program: %s\n"
	   "Pid: %d\n"
           "Trace: %s\n"
           "Version: %s\n"
           "***EOM***\n", 
136
	   formatTimeStampString() , 
137 138
           exit_st_msg,
	   exit_msg, exit_cl_msg,
139 140 141
	   faultID, 
	   (problemData == NULL) ? "" : problemData, 
	   objRef, 
142
	   my_progname, 
143
	   processId, 
144 145
	   theNameOfTheTraceFile ? theNameOfTheTraceFile : "<no tracefile>",
		       NDB_VERSION_STRING);
146 147 148 149 150 151 152 153 154 155 156

  // Add trailing blanks to get a fixed lenght of the message
  while (strlen(messptr) <= MESSAGE_LENGTH-3){
    strcat(messptr, " ");
  }
  
  strcat(messptr, "\n");
  
  return;
}

157 158 159 160 161 162 163 164
NdbShutdownType ErrorReporter::s_errorHandlerShutdownType = NST_ErrorHandler;

void
ErrorReporter::setErrorHandlerShutdownType(NdbShutdownType nst)
{
  s_errorHandlerShutdownType = nst;
}

165 166
void childReportError(int error);

167
void
168
ErrorReporter::handleAssert(const char* message, const char* file, int line, int ec)
169 170 171
{
  char refMessage[100];

autotest@mc01.ndb.mysql.com's avatar
autotest@mc01.ndb.mysql.com committed
172
#ifdef NO_EMULATED_JAM
173
  BaseString::snprintf(refMessage, 100, "file: %s lineNo: %d",
autotest@mc01.ndb.mysql.com's avatar
autotest@mc01.ndb.mysql.com committed
174 175
	   file, line);
#else
176 177 178
  const Uint32 blockNumber = theEmulatedJamBlockNumber;
  const char *blockName = getBlockName(blockNumber);

179
  BaseString::snprintf(refMessage, 100, "%s line: %d (block: %s)",
180
	   file, line, blockName);
autotest@mc01.ndb.mysql.com's avatar
autotest@mc01.ndb.mysql.com committed
181
#endif
182
  WriteMessage(ec, message, refMessage,
183 184
	       theEmulatedJamIndex, theEmulatedJam);

185
  childReportError(ec);
186

187
  NdbShutdown(s_errorHandlerShutdownType);
188
}
189 190

void
191
ErrorReporter::handleError(int messageID,
192 193 194 195
			   const char* problemData, 
			   const char* objRef,
			   NdbShutdownType nst)
{
196
  WriteMessage(messageID, problemData,
197
	       objRef, theEmulatedJamIndex, theEmulatedJam);
198 199 200 201

  childReportError(messageID);

  if(messageID == NDBD_EXIT_ERROR_INSERT){
202 203
    NdbShutdown(NST_ErrorInsert);
  } else {
204 205
    if (nst == NST_ErrorHandler)
      nst = s_errorHandlerShutdownType;
206 207 208 209 210
    NdbShutdown(nst);
  }
}

int 
211
WriteMessage(int thrdMessageID,
212 213 214 215 216 217 218
	     const char* thrdProblemData, const char* thrdObjRef,
	     Uint32 thrdTheEmulatedJamIndex,
	     Uint8 thrdTheEmulatedJam[]){
  FILE *stream;
  unsigned offset;
  unsigned long maxOffset;  // Maximum size of file.
  char theMessage[MESSAGE_LENGTH];
219 220 221 222

  /**
   * Format trace file name
   */
223 224 225 226
  char *theTraceFileName= 0;
  if (globalData.ownId > 0)
    theTraceFileName= NdbConfig_TraceFileName(globalData.ownId,
					      ErrorReporter::get_trace_no());
227
  NdbAutoPtr<char> tmp_aptr1(theTraceFileName);
228 229 230 231 232 233
  
  // The first 69 bytes is info about the current offset
  Uint32 noMsg = globalEmulatorData.theConfiguration->maxNoOfErrorLogs();

  maxOffset = (69 + (noMsg * MESSAGE_LENGTH));
  
234 235 236
  char *theErrorFileName= (char *)NdbConfig_ErrorFileName(globalData.ownId);
  NdbAutoPtr<char> tmp_aptr2(theErrorFileName);

237 238 239 240 241 242
  stream = fopen(theErrorFileName, "r+");
  if (stream == NULL) { /* If the file could not be opened. */
    
    // Create a new file, and skip the first 69 bytes, 
    // which are info about the current offset
    stream = fopen(theErrorFileName, "w");
stewart@mysql.com's avatar
stewart@mysql.com committed
243 244 245 246 247
    if(stream == NULL)
    {
      fprintf(stderr,"Unable to open error log file: %s\n", theErrorFileName);
      return -1;
    }
248 249 250 251
    fprintf(stream, "%s%u%s", "Current byte-offset of file-pointer is: ", 69,
	    "                        \n\n\n");   
    
    // ...and write the error-message...
252
    ErrorReporter::formatMessage(thrdMessageID,
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
				 thrdProblemData, thrdObjRef,
				 theTraceFileName, theMessage);
    fprintf(stream, "%s", theMessage);
    fflush(stream);
    
    /* ...and finally, at the beginning of the file, 
       store the position where to
       start writing the next message. */
    offset = ftell(stream);
    // If we have not reached the maximum number of messages...
    if (offset <= (maxOffset - MESSAGE_LENGTH)){
      fseek(stream, 40, SEEK_SET);
      // ...set the current offset...
      fprintf(stream,"%d", offset);
    } else {
      fseek(stream, 40, SEEK_SET);
      // ...otherwise, start over from the beginning.
      fprintf(stream, "%u%s", 69, "             ");
    }
  } else {
    // Go to the latest position in the file...
    fseek(stream, 40, SEEK_SET);
    fscanf(stream, "%u", &offset);
    fseek(stream, offset, SEEK_SET);
    
    // ...and write the error-message there...
279
    ErrorReporter::formatMessage(thrdMessageID,
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
				 thrdProblemData, thrdObjRef,
				 theTraceFileName, theMessage);
    fprintf(stream, "%s", theMessage);
    fflush(stream);
    
    /* ...and finally, at the beginning of the file, 
       store the position where to
       start writing the next message. */
    offset = ftell(stream);
    
    // If we have not reached the maximum number of messages...
    if (offset <= (maxOffset - MESSAGE_LENGTH)){
      fseek(stream, 40, SEEK_SET);
      // ...set the current offset...
      fprintf(stream,"%d", offset);
    } else {
      fseek(stream, 40, SEEK_SET);
      // ...otherwise, start over from the beginning.
      fprintf(stream, "%u%s", 69, "             ");
    }
  }
  fflush(stream);
  fclose(stream);
  
304 305 306
  if (theTraceFileName) {
    // Open the tracefile...
    FILE *jamStream = fopen(theTraceFileName, "w");
307
  
308 309 310 311 312
    //  ...and "dump the jam" there.
    //  ErrorReporter::dumpJam(jamStream);
    if(thrdTheEmulatedJam != 0){
      dumpJam(jamStream, thrdTheEmulatedJamIndex, thrdTheEmulatedJam);
    }
313
  
314 315 316 317 318 319 320 321
    /* Dont print the jobBuffers until a way to copy them, 
       like the other variables,
       is implemented. Otherwise when NDB keeps running, 
       with this function running
       in the background, the jobBuffers will change during runtime. And when
       they're printed here, they will not be correct anymore.
    */
    globalScheduler.dumpSignalMemory(jamStream);
322
  
323 324 325
    fclose(jamStream);
  }

326 327 328 329 330 331 332
  return 0;
}

void 
dumpJam(FILE *jamStream, 
	Uint32 thrdTheEmulatedJamIndex, 
	Uint8 thrdTheEmulatedJam[]) {
autotest@mc01.ndb.mysql.com's avatar
autotest@mc01.ndb.mysql.com committed
333
#ifndef NO_EMULATED_JAM   
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
  // print header
  const int maxaddr = 8;
  fprintf(jamStream, "JAM CONTENTS up->down left->right ?=not block entry\n");
  fprintf(jamStream, "%-7s ", "BLOCK");
  for (int i = 0; i < maxaddr; i++)
    fprintf(jamStream, "%-6s ", "ADDR");
  fprintf(jamStream, "\n");

  // treat as array of Uint32
  const Uint32 *base = (Uint32 *)thrdTheEmulatedJam;
  const int first = thrdTheEmulatedJamIndex / sizeof(Uint32);	// oldest
  int cnt, idx;

  // look for first block entry
  for (cnt = 0, idx = first; cnt < EMULATED_JAM_SIZE; cnt++, idx++) {
    if (idx >= EMULATED_JAM_SIZE)
      idx = 0;
    const Uint32 aJamEntry = base[idx];
    if (aJamEntry > (1 << 20))
      break;
  }

  // 1. if first entry is a block entry, it is printed in the main loop
  // 2. else if any block entry exists, the jam starts in an unknown block
  // 3. else if no block entry exists, the block is theEmulatedJamBlockNumber
  // a "?" indicates first addr is not a block entry
  if (cnt == 0)
    ;
  else if (cnt < EMULATED_JAM_SIZE)
    fprintf(jamStream, "%-7s?", "");
  else {
    const Uint32 aBlockNumber = theEmulatedJamBlockNumber;
    const char *aBlockName = getBlockName(aBlockNumber);
    if (aBlockName != 0)
      fprintf(jamStream, "%-7s?", aBlockName);
    else
      fprintf(jamStream, "0x%-5X?", aBlockNumber);
  }

  // loop over all entries
  int cntaddr = 0;
  for (cnt = 0, idx = first; cnt < EMULATED_JAM_SIZE; cnt++, idx++) {
    globalData.incrementWatchDogCounter(4);	// watchdog not to kill us ?
    if (idx >= EMULATED_JAM_SIZE)
      idx = 0;
    const Uint32 aJamEntry = base[idx];
    if (aJamEntry > (1 << 20)) {
      const Uint32 aBlockNumber = aJamEntry >> 20;
      const char *aBlockName = getBlockName(aBlockNumber);
      if (cnt > 0)
	  fprintf(jamStream, "\n");
      if (aBlockName != 0)
	fprintf(jamStream, "%-7s ", aBlockName);
      else
	fprintf(jamStream, "0x%-5X ", aBlockNumber);
      cntaddr = 0;
    }
    if (cntaddr == maxaddr) {
      fprintf(jamStream, "\n%-7s ", "");
      cntaddr = 0;
    }
    fprintf(jamStream, "%06u ", aJamEntry & 0xFFFFF);
    cntaddr++;
  }
  fprintf(jamStream, "\n");
  fflush(jamStream);
autotest@mc01.ndb.mysql.com's avatar
autotest@mc01.ndb.mysql.com committed
400
#endif // ifndef NO_EMULATED_JAM
401
}