os/kernelhwsrv/kerneltest/e32utils/d_exc/d_exc.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     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 	}