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