kerneltest/e32utils/d_exc/d_exc.cpp
changeset 0 a41df078684a
equal deleted inserted replaced
-1:000000000000 0:a41df078684a
       
     1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of the License "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // e32utils\d_exc\d_exc.cpp
       
    15 // Trap and log user-side exceptions and panics.
       
    16 // USAGE:
       
    17 // d_exc
       
    18 // Trap panics and exceptions forever.  Prompt whether to log.
       
    19 // Logs go on C: drive.
       
    20 // d_exc [-m] [-nN] [-pN] [-b] [-d log_path]
       
    21 // -m	minimal logging (no stack dump)
       
    22 // -nN	stop after N exceptions/panics
       
    23 // -pN	log to serial port N instead of C: drive
       
    24 // -b	do not prompt; always log
       
    25 // -d  specify the path for log files.  If not given, logs are
       
    26 // written to the root of the system drive.  If just a path
       
    27 // name is given, logs are written to that directory (must
       
    28 // start with a \) on the system drive.
       
    29 // 
       
    30 //
       
    31 
       
    32 #include <e32std.h>
       
    33 #include <e32std_private.h>
       
    34 #include <e32svr.h>
       
    35 #include <d32comm.h>
       
    36 #include <f32file.h>
       
    37 #include "minkda.h"
       
    38 
       
    39 RNotifier Notifier;				// The "UI"
       
    40 RMinKda Trapper;
       
    41 RFs FileSession;
       
    42 TBuf16<KMaxFileName> LogPath; // to specify log file location
       
    43 
       
    44 // Possible outputs where crash information can be dumped
       
    45 enum TOutputType{ EFile, ESerial };
       
    46 
       
    47 // Variables shared between DumpLine() and the various functions used
       
    48 // to format crash info.
       
    49 TOutputType ActiveOutput = EFile;	
       
    50 TBool IoError;						// ETrue after I/O error
       
    51 RBusDevComm CommPort;				// Handle to serial port used
       
    52 RFile File;							// Handle to text file used
       
    53 
       
    54 // Maximum length in characters of a line in the file containing
       
    55 // textual information about the crash.
       
    56 const TInt KMaxLineLength = KMaxFullName + 32;
       
    57 
       
    58 class TLexNew : public TLex16
       
    59 	{
       
    60 public:
       
    61 	inline TLexNew(const TDesC16& aDes) {Assign(aDes);}
       
    62 	TInt ExtractParameter(TDes16 &aParam);
       
    63 	};
       
    64 
       
    65 TInt TLexNew::ExtractParameter(TDes16 &aParam)
       
    66 	{
       
    67 	TBuf16<512> token;
       
    68 	TBuf16<512> param;
       
    69 
       
    70 	TBool GetNext = EFalse;
       
    71 
       
    72 	//exit..if it's empty (empty option at the end of command)
       
    73 	if (!Peek())
       
    74 		return KErrArgument;
       
    75 
       
    76 	// remove any space between option and the rest of param..
       
    77 	SkipSpace();
       
    78 
       
    79 	// just see, what's next.. 
       
    80 	// if there this a param with spaces- should be in "quotes"
       
    81 	if (Peek() == '"')
       
    82 		{
       
    83 		GetNext = ETrue;
       
    84 		Inc(); // skip this quote " and move to next position..   
       
    85 		}
       
    86 
       
    87 	// remove spaces after quotes ("  param...")
       
    88 	SkipSpace();
       
    89 
       
    90 	// ..mark next character position as a start of our token 
       
    91 	Mark();
       
    92 
       
    93 	// move until the end of our token (next space).. 
       
    94 	SkipCharacters();
       
    95 
       
    96 	//and get it!!
       
    97 	token.Copy(MarkedToken());
       
    98 
       
    99 	// if.. there was one-word param.. with quotes..shrink it..and don't try to search next one..
       
   100 	if (*(token.MidTPtr(token.Length()-1).Ptr()) == '"')
       
   101 		{
       
   102 		// just shrink it by that ending quote..
       
   103 		token.SetLength(token.Length()-1);
       
   104 		GetNext=EFalse;
       
   105 		}
       
   106 
       
   107 	// This is at least beginning of our param.. let's use it!
       
   108 	// add this to beginning of our param..
       
   109 	param.Append(token);
       
   110 
       
   111 	// if this was param specified in quotes..search for the ending quote..
       
   112 	while (GetNext)
       
   113 		{
       
   114 		// Next is space.. 
       
   115 		SkipSpace();
       
   116 
       
   117 		// before taking next one..check it - if '-' on the beginning..
       
   118 		// it's either next param specifier..(no ending quote at all) 
       
   119 		if (Peek() == '-')
       
   120 			return KErrArgument;
       
   121 
       
   122 		// get the next one..
       
   123 		token.Copy(NextToken());
       
   124 
       
   125 		// was there any token more? ..if not- we're at the end..
       
   126 		// so the ending quote still wasn't found...
       
   127 		if (!token.Length())
       
   128 			return KErrArgument;
       
   129 
       
   130 		// is this the last one - with quote" at the end?
       
   131 		if (*(token.MidTPtr(token.Length()-1).Ptr()) == '"')
       
   132 			{
       
   133 			// just shrink it by that ending quote..
       
   134 			token.SetLength(token.Length()-1);
       
   135 			GetNext=EFalse;
       
   136 			}
       
   137 
       
   138 		param.Append(_L(" ")); // there was space in orig. param..restore it..
       
   139 		param.Append(token); // and append this token to our param..
       
   140 		}
       
   141 
       
   142 	// if there was any space at the end..(e.g. if specified: -d"c:\logs  ")
       
   143 	// - remove it
       
   144 	param.TrimRight();
       
   145 
       
   146 	//finally - copy param to the referenced descriptor
       
   147 	aParam.Copy(param);
       
   148 
       
   149 	return KErrNone;
       
   150 	}
       
   151 
       
   152 TInt ValidatePath(TDes16 &aLogPath)
       
   153 	{
       
   154 	
       
   155 	// check the length first.. (20 chars for file name..)
       
   156 	if (aLogPath.Length() >(KMaxFileName - 20))
       
   157 		{
       
   158 		Notifier.InfoPrint(_L("directory name too long.."));
       
   159 		return KErrArgument;
       
   160 		}
       
   161 
       
   162 	// if it hasn't drive letter (colon wasn't second..)
       
   163 	if (*(aLogPath.MidTPtr(1).Ptr()) != ':')
       
   164 		{
       
   165 		// if it starts with "\" use system drive.. 
       
   166 		if (*(aLogPath.MidTPtr(0).Ptr()) == '\\')
       
   167 			{
       
   168 			// if someone specified param like: "\ path\" ...obviously..
       
   169 			if (*(aLogPath.MidTPtr(1).Ptr()) == ' ')
       
   170 				return KErrArgument;
       
   171 
       
   172 			TBuf16<2> drive;
       
   173 			drive.Append(RFs::GetSystemDriveChar());
       
   174 			drive.LowerCase();
       
   175 			drive.Append(_L(":"));
       
   176 			aLogPath.Insert(0, drive);
       
   177 			}
       
   178 		else //otherwise -path not valid.. 
       
   179 			{
       
   180 			return KErrArgument;
       
   181 			}
       
   182 		}
       
   183 
       
   184 	// and add backslash if needed
       
   185 	if (*(aLogPath.MidTPtr(aLogPath.Length()-1).Ptr()) != '\\')
       
   186 		aLogPath.Append(_L("\\"));
       
   187 
       
   188 	//open file session..
       
   189 	if (FileSession.Connect() != KErrNone)
       
   190 		return KErrGeneral;
       
   191 
       
   192 	RDir dir;
       
   193 	TInt err=KErrNone;
       
   194 	if (dir.Open(FileSession, aLogPath, KEntryAttMatchExclusive) != KErrNone)
       
   195 		{
       
   196 		Notifier.InfoPrint(_L("specified directory doesn't exist"));
       
   197 		LogPath.Zero(); //clear global path..
       
   198 		err = KErrArgument;
       
   199 		}
       
   200 	else
       
   201 		{
       
   202 		dir.Close();
       
   203 		}
       
   204 
       
   205 	// close file session..
       
   206 	FileSession.Close();
       
   207 
       
   208 	return err;
       
   209 	}
       
   210 
       
   211 
       
   212 // Open specified serial port and push handle on the cleanup stack.
       
   213 
       
   214 void OpenCommPortLC(TInt aPortNum)
       
   215 	{
       
   216 #ifdef __WINS__	
       
   217 	_LIT(KPdd, "ECDRV");
       
   218 #else
       
   219 	_LIT(KPdd, "EUART");
       
   220 #endif
       
   221 	_LIT(KLdd, "ECOMM");
       
   222 	_LIT(KErrPdd, "Failed to load serial PDD");
       
   223 	_LIT(KErrLdd, "Failed to load serial LDD");
       
   224 	_LIT(KErrOpen, "Failed to open comm port");
       
   225 	_LIT(KErrCfg, "Failed to configure comm port");
       
   226 
       
   227 	TInt r = User::LoadPhysicalDevice(KPdd);
       
   228 	if (r != KErrNone && r != KErrAlreadyExists)
       
   229 		{
       
   230 		Notifier.InfoPrint(KErrPdd);
       
   231 		User::Leave(r);
       
   232 		}
       
   233 
       
   234 	r = User::LoadLogicalDevice(KLdd);
       
   235 	if (r != KErrNone && r != KErrAlreadyExists)
       
   236 		{
       
   237 		Notifier.InfoPrint(KErrLdd);
       
   238 		User::Leave(r);
       
   239 		}
       
   240 
       
   241 	r = CommPort.Open(aPortNum);
       
   242 	if (r != KErrNone)
       
   243 		{
       
   244 		Notifier.InfoPrint(KErrOpen);
       
   245 		User::Leave(r);
       
   246 		}
       
   247 	CleanupClosePushL(CommPort);
       
   248 
       
   249 	TCommConfig cfgBuf;
       
   250 	TCommConfigV01& cfg=cfgBuf();
       
   251 	CommPort.Config(cfgBuf);
       
   252 	cfg.iRate=EBps115200;
       
   253 	cfg.iDataBits=EData8;
       
   254 	cfg.iStopBits=EStop1;
       
   255 	cfg.iParity=EParityNone;
       
   256 	cfg.iHandshake=KConfigObeyXoff|KConfigSendXoff;
       
   257 	cfg.iFifo=EFifoEnable;
       
   258 	cfg.iTerminatorCount=0;
       
   259 	cfg.iSIREnable=ESIRDisable;
       
   260 	r = CommPort.SetConfig(cfgBuf);
       
   261 	if (r != KErrNone)
       
   262 		{
       
   263 		Notifier.InfoPrint(KErrCfg);
       
   264 		User::Leave(r);
       
   265 		}
       
   266 	}
       
   267 
       
   268 
       
   269 void ParseCmdLineL(TInt& aPortNum, TInt& aMaxTrapCount, TBool& aInteractive, TBool& aDumpStack)
       
   270 	{
       
   271 	_LIT(KInvalidArg, "Invalid command-line");
       
   272 
       
   273 	HBufC* cl = HBufC::NewLC(User::CommandLineLength());
       
   274 	TPtr clp = cl->Des();
       
   275 	User::CommandLine(clp);
       
   276 
       
   277 	// If started from UIKON shell, ignore command-line and use defaults
       
   278 	if (clp.Match(_L("?:\\*")) == 0)
       
   279 		return;
       
   280 
       
   281 	TLexNew lex(*cl);
       
   282 
       
   283 	while (! lex.Eos())
       
   284 		{
       
   285 		TInt r = KErrArgument;
       
   286 		if (lex.Get() == '-')
       
   287 			{
       
   288 			switch (lex.Get())
       
   289 				{
       
   290 			case 'n':
       
   291 				r = lex.Val(aMaxTrapCount);
       
   292 				break;
       
   293 			case 'p':
       
   294 				r = lex.Val(aPortNum);
       
   295 				if (r == KErrNone)
       
   296 					ActiveOutput = ESerial;
       
   297 				break;
       
   298 			case 'b':
       
   299 				aInteractive = EFalse;
       
   300 				r = KErrNone;
       
   301 				break;
       
   302 			case 'm':
       
   303 				aDumpStack = EFalse;
       
   304 				r = KErrNone;
       
   305 				break;
       
   306 			case 'd':
       
   307 				//try to extract path and store it in global buffer
       
   308 				r = lex.ExtractParameter(LogPath);
       
   309 				// check, if specified path is valid
       
   310 				if (r == KErrNone)  
       
   311 					r = ValidatePath(LogPath);  
       
   312 				break;
       
   313 				}
       
   314 			}
       
   315 		if (r != KErrNone)
       
   316 			{
       
   317 			Notifier.InfoPrint(KInvalidArg);
       
   318 			User::Leave(KErrArgument);
       
   319 			}
       
   320 		lex.SkipSpace();
       
   321 		}
       
   322 
       
   323 	CleanupStack::PopAndDestroy(cl);
       
   324 	}
       
   325 
       
   326 
       
   327 // Dump specified line + CRLF on the selected output.  Set IoError to
       
   328 // ETrue if an error occurs.
       
   329 
       
   330 void DumpLine(TDes8& aLine)
       
   331 	{
       
   332 	TInt r;
       
   333 	_LIT8(KCrLf, "\r\n");
       
   334 	aLine.Append(KCrLf);
       
   335 	if (ActiveOutput == ESerial)
       
   336 		{
       
   337 		TRequestStatus s;
       
   338 		CommPort.Write(s, aLine);
       
   339 		User::WaitForRequest(s);
       
   340 		r = s.Int();
       
   341 		}
       
   342 	else
       
   343 		r = File.Write(aLine);
       
   344 	if (r != KErrNone)
       
   345 		IoError = ETrue;
       
   346 	}
       
   347 
       
   348 
       
   349 void DumpExcInfo(const TDbgCpuExcInfo& aInfo, TDes8& aLine)
       
   350 	{
       
   351 	_LIT8(KHdr, "\r\nUNHANDLED EXCEPTION:");
       
   352 	aLine = KHdr;
       
   353 	DumpLine(aLine);
       
   354 #ifdef __MARM__
       
   355 	_LIT8(KFmt1, "code=%d PC=%08x FAR=%08x FSR=%08x");
       
   356 	aLine.Format(KFmt1, aInfo.iExcCode, aInfo.iFaultPc, aInfo.iFaultAddress, aInfo.iFaultStatus);
       
   357 	DumpLine(aLine);
       
   358 	_LIT8(KFmt2, "R13svc=%08x R14svc=%08x SPSRsvc=%08x");
       
   359 	aLine.Format(KFmt2, aInfo.iR13Svc, aInfo.iR14Svc, aInfo.iSpsrSvc); 
       
   360 	DumpLine(aLine);
       
   361 #else
       
   362 	(void) aInfo; // silence warning
       
   363 #endif
       
   364 	}
       
   365 
       
   366 
       
   367 void DumpRegisters(const TDbgRegSet& aRegs, TDes8& aLine)
       
   368 	{
       
   369 #if defined(__MARM__)
       
   370 	_LIT8(KHdr, "\r\nUSER REGISTERS:");
       
   371 	aLine = KHdr;
       
   372 	DumpLine(aLine);
       
   373 	_LIT8(KFmtCpsr, "CPSR=%08x");
       
   374 	aLine.Format(KFmtCpsr, aRegs.iCpsr);
       
   375 	DumpLine(aLine);
       
   376 	for (TInt i=0; i<TDbgRegSet::KRegCount; i+=4)
       
   377 		{
       
   378 		_LIT8(KFmtReg, "r%02d=%08x %08x %08x %08x");
       
   379 		aLine.Format(KFmtReg, i, aRegs.iRn[i], aRegs.iRn[i+1], aRegs.iRn[i+2], aRegs.iRn[i+3]);
       
   380 		DumpLine(aLine);
       
   381 		}
       
   382 #else
       
   383 	(void) aRegs; // silence warnings
       
   384 	(void) aLine; 
       
   385 #endif
       
   386 	}
       
   387 
       
   388 
       
   389 void DumpCodeSegs(TUint aPid, TDes8& aLine)
       
   390 	{
       
   391 	_LIT(KPanicCodeMods, "DEXC-CODEMOD");
       
   392 	_LIT8(KHdr, "\r\nCODE SEGMENTS:");
       
   393 	_LIT8(KFmtOverflow, "Only first %d code modules displayed");
       
   394 	_LIT8(KFmtMod, "%08X-%08X %S");
       
   395 
       
   396 	aLine = KHdr;
       
   397 	DumpLine(aLine);
       
   398 
       
   399 	// :FIXME: improve API
       
   400 	// :FIXME: suspend/resume all threads in process
       
   401 	const TInt KMaxCount = 128;
       
   402 	TAny* handles[KMaxCount];
       
   403 	TInt c = KMaxCount;
       
   404 
       
   405 	TInt r = Trapper.GetCodeSegs(aPid, handles, c);
       
   406 	__ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicCodeMods, r));
       
   407 
       
   408 	if (c > KMaxCount)
       
   409 		{
       
   410 		aLine.Format(KFmtOverflow, c);
       
   411 		DumpLine(aLine);
       
   412 		c = KMaxCount;
       
   413 		}
       
   414 
       
   415 	for (TInt i=0; i<c; i++)
       
   416 		{
       
   417 		TDbgCodeSegInfo info;
       
   418 		r = Trapper.GetCodeSegInfo(handles[i], aPid, info);
       
   419 		if (r == KErrNone)
       
   420 			{
       
   421 			TBuf8<KMaxFileName> path;
       
   422 			path.Copy(info.iPath);
       
   423 			aLine.Format(KFmtMod, info.iCodeBase, info.iCodeBase+info.iCodeSize, &path);
       
   424 			DumpLine(aLine);
       
   425 			}
       
   426 		}
       
   427 	}
       
   428 
       
   429 
       
   430 void DumpTextInfo(const TDbgCrashInfo& aCrashInfo, const TDbgThreadInfo& aThreadInfo)
       
   431 	{
       
   432 	_LIT(KFmtTextFile, "d_exc_%d.txt");
       
   433 	_LIT(KErrTextOpen, "text file open error");
       
   434 	_LIT(KErrTextWrite, "text file write error");
       
   435 
       
   436 	if (ActiveOutput == EFile)
       
   437 		{
       
   438 		TBuf16<KMaxFileName> name;
       
   439 		name.Format(KFmtTextFile, aCrashInfo.iTid);
       
   440 		
       
   441 		// if -d param wasn't specified, use default location..(root dir on system drive)
       
   442 		if(!LogPath.Length())
       
   443 			{
       
   444 			LogPath.Append(RFs::GetSystemDriveChar());
       
   445 			LogPath.LowerCase();
       
   446 			LogPath.Append(_L(":\\"));
       
   447 			}
       
   448 
       
   449 		TBuf16<KMaxFileName> filename;
       
   450 		filename.Copy(LogPath); 
       
   451 		filename.Append(name);
       
   452 
       
   453 		TInt r = File.Replace(FileSession, filename, EFileWrite+EFileShareAny+EFileStream);
       
   454 		if (r != KErrNone)
       
   455 			{
       
   456 			Notifier.InfoPrint(KErrTextOpen);
       
   457 			return;
       
   458 			}
       
   459 		}
       
   460 
       
   461 	IoError = EFalse;
       
   462 
       
   463 	// Note that following buffer is passed to callee functions and
       
   464 	// reuse to minimise stack footprint.
       
   465 	TBuf8<KMaxLineLength> line;
       
   466 
       
   467 	line.Fill('-', 76);
       
   468 	DumpLine(line);
       
   469 	_LIT8(KHdr, "EKA2 USER CRASH LOG");
       
   470 	line = KHdr;
       
   471 	DumpLine(line);
       
   472 	line.Copy(aThreadInfo.iFullName);
       
   473 	_LIT8(KName, "Thread Name: ");
       
   474 	line.Insert(0, KName);
       
   475 	DumpLine(line);
       
   476 	_LIT8(KFmtTid, "Thread ID: %u");
       
   477 	line.Format(KFmtTid, aCrashInfo.iTid);
       
   478 	DumpLine(line);
       
   479 	_LIT8(KFmtStack, "User Stack %08X-%08X");
       
   480 	line.Format(KFmtStack, aThreadInfo.iStackBase,
       
   481 				aThreadInfo.iStackBase+aThreadInfo.iStackSize);
       
   482 	DumpLine(line);
       
   483 
       
   484 	if (aCrashInfo.iType == TDbgCrashInfo::EPanic)
       
   485 		{
       
   486 		TBuf8<KMaxExitCategoryName> cat;
       
   487 		cat.Copy(aThreadInfo.iExitCategory);
       
   488 		_LIT8(KFmtPanic, "Panic: %S-%d");
       
   489 		line.Format(KFmtPanic, &cat, aThreadInfo.iExitReason);
       
   490 		DumpLine(line);
       
   491 		}
       
   492 	else
       
   493 		DumpExcInfo(aCrashInfo.iCpu, line);
       
   494 
       
   495 	DumpRegisters(aThreadInfo.iCpu, line);
       
   496 	DumpCodeSegs(aThreadInfo.iPid, line);
       
   497 
       
   498 	line.Zero();
       
   499 	DumpLine(line);
       
   500 
       
   501 	if (IoError)
       
   502 		Notifier.InfoPrint(KErrTextWrite);
       
   503 
       
   504 	if (ActiveOutput == EFile)
       
   505 		File.Close();
       
   506 	}
       
   507 
       
   508 
       
   509 // Output stack on selected output.  If serial port, use
       
   510 // human-readable format.  If file, use binary format.
       
   511 
       
   512 void DumpStack(TUint aTid, const TDbgThreadInfo& aInfo)
       
   513 	{
       
   514 	_LIT(KFmtStackFile, "d_exc_%d.stk");
       
   515 	_LIT(KErrStackOpen, "stack file open error");
       
   516 	_LIT(KErrStackWrite, "stack file write error");
       
   517 	_LIT(KPanicReadStack, "DEXC-READSTACK");
       
   518 
       
   519 	TInt r;
       
   520 	IoError = EFalse;
       
   521 
       
   522 	RFile file;
       
   523 	if (ActiveOutput == EFile)
       
   524 		{
       
   525 		TBuf16<KMaxFileName> name;
       
   526 		name.Format(KFmtStackFile, aTid);
       
   527 
       
   528 		// if -d param wasn't specified, use default location..(root dir on system drive)
       
   529 		if(!LogPath.Length())
       
   530 			{
       
   531 			LogPath.Append(RFs::GetSystemDriveChar());
       
   532 			LogPath.LowerCase();
       
   533 			LogPath.Append(_L(":\\"));
       
   534 			}
       
   535 
       
   536 		TBuf16<KMaxFileName> filename;
       
   537 		filename.Copy(LogPath); 
       
   538 		filename.Append(name);
       
   539 
       
   540 		r = file.Replace(FileSession, filename, EFileWrite+EFileShareAny+EFileStream);
       
   541 		if (r != KErrNone)
       
   542 			{
       
   543 			Notifier.InfoPrint(KErrStackOpen);
       
   544 			return;
       
   545 			}
       
   546 		}
       
   547 
       
   548 	const TInt KBufSize = 256;
       
   549 	TBuf8<KBufSize> buf;
       
   550 	TLinAddr top = aInfo.iStackBase + aInfo.iStackSize;
       
   551 	for (TLinAddr base = aInfo.iStackBase; base < top; base += KBufSize)
       
   552 		{
       
   553 		// Read chunk of stack.  Should always succeeds as thread has
       
   554 		// been suspended by LDD.
       
   555 		r = Trapper.ReadMem(aTid, base, buf);
       
   556 		__ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicReadStack, r));
       
   557 
       
   558 		if (ActiveOutput == ESerial)
       
   559 			{
       
   560 			TBuf8<80> out;
       
   561 			TBuf8<20> ascii;
       
   562 			TUint a = base;
       
   563 			TInt len = buf.Length();
       
   564 			TInt offset = 0;
       
   565 			while(len>0)
       
   566 				{
       
   567 				out.Zero();
       
   568 				ascii.Zero();
       
   569 				out.AppendNumFixedWidth(a,EHex,8);
       
   570 				out.Append(_L8(": "));
       
   571 				TUint b;
       
   572 				for (b=0; b<16; b++)
       
   573 					{
       
   574 					TUint8 c=*(buf.Ptr()+offset+b);
       
   575 					out.AppendNumFixedWidth(c,EHex,2);
       
   576 					out.Append(' ');
       
   577 					if (c<0x20 || c>=0x7f)
       
   578 						c=0x2e;
       
   579 					ascii.Append(TChar(c));
       
   580 					}
       
   581 				out.Append(ascii);
       
   582 				DumpLine(out);
       
   583 				a+=16;
       
   584 				offset += 16;
       
   585 				len-=16;
       
   586 				} 
       
   587 			}
       
   588 		else
       
   589 			{
       
   590 			if (file.Write(buf) != KErrNone)
       
   591 				IoError = ETrue;
       
   592 			}
       
   593 		}
       
   594 
       
   595 	if (IoError)
       
   596 		Notifier.InfoPrint(KErrStackWrite);
       
   597 	if (ActiveOutput == EFile)
       
   598 		file.Close();
       
   599 	}
       
   600 
       
   601 
       
   602 // Display a dialog box containing basic facts about the crash and ask
       
   603 // the user whether to dump detailed information or skip this crash.
       
   604 
       
   605 enum TDebugChoice { EDoDebug, EDoNotDebug }; 
       
   606 
       
   607 TDebugChoice CrashDialog(TDbgCrashInfo::TType aCrashType, const TDbgThreadInfo& aInfo)
       
   608 	{
       
   609 	_LIT(KExc, "Exception");
       
   610 	_LIT(KPanic, "Panic %S:%d");
       
   611 	_LIT(KBut1, "Do Not Debug");
       
   612 	_LIT(KBut2, "Debug");
       
   613 
       
   614 	TBuf<64> line1;
       
   615 	if (aCrashType == TDbgCrashInfo::EException)
       
   616 		line1 = KExc;
       
   617 	else
       
   618 		line1.Format(KPanic, &aInfo.iExitCategory, aInfo.iExitReason);
       
   619 	TInt r;
       
   620 	TRequestStatus s;
       
   621 	Notifier.Notify(line1, aInfo.iFullName, KBut1, KBut2, r, s);
       
   622 	User::WaitForRequest(s);
       
   623 	return r == 0 ? EDoNotDebug : EDoDebug;
       
   624 	}
       
   625 
       
   626 
       
   627 void MainL()
       
   628 	{
       
   629 	_LIT(KErrFs, "Failed to connect to file server");
       
   630 	_LIT(KErrLoadLdd, "Failed to load KDA LDD");
       
   631 	_LIT(KErrOpenLdd, "Failed to open KDA LDD");
       
   632 	_LIT(KLddPath, "MINKDA");
       
   633 	_LIT(KStarted, "D_EXC started");
       
   634 	_LIT(KCrash, "Crash detected");
       
   635 	_LIT(KPanicThreadInfo, "DEXC-THREADINFO");
       
   636 
       
   637 	TInt portNum;
       
   638 	TInt maxTrapCount = -1;
       
   639 	TBool isInteractive = ETrue;
       
   640 	TBool dumpStack = ETrue;
       
   641 	ParseCmdLineL(portNum, maxTrapCount, isInteractive, dumpStack);
       
   642 
       
   643 	// Open selected output and push resulting handle on cleanup
       
   644 	// stack.
       
   645 	TInt r;
       
   646 	if (ActiveOutput == EFile)
       
   647 		{
       
   648 		if ((r = FileSession.Connect()) != KErrNone)
       
   649 			{
       
   650 			Notifier.InfoPrint(KErrFs);
       
   651 			User::Leave(r);
       
   652 			}
       
   653 		CleanupClosePushL(FileSession);
       
   654 		}
       
   655 	else
       
   656 		OpenCommPortLC(portNum);
       
   657 
       
   658 	r = User::LoadLogicalDevice(KLddPath);
       
   659 	if (r != KErrNone && r != KErrAlreadyExists)
       
   660 		{
       
   661 		Notifier.InfoPrint(KErrLoadLdd);
       
   662 		User::Leave(r);
       
   663 		}
       
   664 
       
   665 // See comment near __KHEAP_MARKEND
       
   666 //	 __KHEAP_MARK;
       
   667 
       
   668 	r = Trapper.Open();
       
   669 	if (r != KErrNone)
       
   670 		{
       
   671 		Notifier.InfoPrint(KErrOpenLdd);
       
   672 		User::Leave(r);
       
   673 		}
       
   674 	CleanupClosePushL(Trapper);
       
   675 
       
   676 	Notifier.InfoPrint(KStarted);
       
   677 
       
   678 	// Main loop
       
   679 	TRequestStatus s;
       
   680 	TDbgCrashInfo crashInfo;
       
   681 	Trapper.Trap(s, crashInfo);
       
   682 	for (TInt crashCount = 0; maxTrapCount<0 || crashCount<maxTrapCount; ++crashCount)
       
   683 		{
       
   684 		User::WaitForRequest(s);
       
   685 
       
   686 		// Get more info about crashed thread.  Should always succeeds
       
   687 		// as the thread has been suspended by LDD.
       
   688 		TDbgThreadInfo threadInfo;
       
   689 		TInt r = Trapper.GetThreadInfo(crashInfo.iTid, threadInfo);
       
   690 		__ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicThreadInfo, r));
       
   691 
       
   692 		if (! isInteractive)
       
   693 			Notifier.InfoPrint(KCrash);
       
   694 		if (! isInteractive || CrashDialog(crashInfo.iType, threadInfo) == EDoDebug)
       
   695 			{
       
   696 			DumpTextInfo(crashInfo, threadInfo);
       
   697 			if (dumpStack)
       
   698 				DumpStack(crashInfo.iTid, threadInfo);
       
   699 			}
       
   700 		Trapper.Trap(s, crashInfo);
       
   701 		Trapper.KillCrashedThread();
       
   702 		}
       
   703 
       
   704 	Trapper.CancelTrap();
       
   705 
       
   706 	CleanupStack::PopAndDestroy(&Trapper);
       
   707 	CleanupStack::PopAndDestroy(); // FileSession or CommPort
       
   708 	
       
   709 // Commented out because the InfoPrint thread may or may not have
       
   710 // terminated when we reach this point.  It if hasn't a spurious
       
   711 // memory leak will be reported.
       
   712 // #ifdef _DEBUG
       
   713 // 	User::After(3000000);
       
   714 // 	 __KHEAP_MARKEND;
       
   715 // #endif
       
   716 	
       
   717 	User::FreeLogicalDevice(KKdaLddName);
       
   718 	}
       
   719 
       
   720 
       
   721 TInt E32Main()
       
   722 	{
       
   723 	_LIT(KPanicNtf, "DEXC-NO-NTF");
       
   724 	_LIT(KPanicLeave, "DEXC-LEAVE");
       
   725 	_LIT(KPanicOom, "DEXC-NO-CLEANUP");
       
   726 
       
   727 	// :FIXME: remove when platform security is always on
       
   728 	RProcess().DataCaging(RProcess::EDataCagingOn);
       
   729 
       
   730 #ifdef _DEBUG
       
   731 	TInt phcStart;
       
   732 	TInt thcStart;
       
   733 	RThread().HandleCount(phcStart, thcStart);
       
   734 #endif
       
   735 
       
   736 	TInt r = Notifier.Connect();
       
   737 	__ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicNtf, r));
       
   738 
       
   739 	__UHEAP_MARK;
       
   740 	CTrapCleanup* cleanup = CTrapCleanup::New();
       
   741 	__ASSERT_ALWAYS(cleanup, User::Panic(KPanicOom, KErrNoMemory));
       
   742 	TRAP(r, MainL());
       
   743  	__ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicLeave, r));
       
   744 	delete cleanup;
       
   745 	__UHEAP_MARKEND;
       
   746 
       
   747 	Notifier.Close();
       
   748 
       
   749 #ifdef _DEBUG
       
   750 	TInt phcEnd;
       
   751 	TInt thcEnd;
       
   752 	RThread().HandleCount(phcEnd, thcEnd);
       
   753 	__ASSERT_DEBUG(phcStart == phcEnd, User::Panic(_L("DEXC-PHC"), phcEnd-phcStart));
       
   754 	__ASSERT_DEBUG(thcStart == thcEnd, User::Panic(_L("DEXC-THC"), thcEnd-thcStart));
       
   755 #endif
       
   756 
       
   757 	return r;
       
   758 	}