1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/kernelhwsrv/kerneltest/e32utils/d_exc/d_exc.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,758 @@
1.4 +// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of the License "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// e32utils\d_exc\d_exc.cpp
1.18 +// Trap and log user-side exceptions and panics.
1.19 +// USAGE:
1.20 +// d_exc
1.21 +// Trap panics and exceptions forever. Prompt whether to log.
1.22 +// Logs go on C: drive.
1.23 +// d_exc [-m] [-nN] [-pN] [-b] [-d log_path]
1.24 +// -m minimal logging (no stack dump)
1.25 +// -nN stop after N exceptions/panics
1.26 +// -pN log to serial port N instead of C: drive
1.27 +// -b do not prompt; always log
1.28 +// -d specify the path for log files. If not given, logs are
1.29 +// written to the root of the system drive. If just a path
1.30 +// name is given, logs are written to that directory (must
1.31 +// start with a \) on the system drive.
1.32 +//
1.33 +//
1.34 +
1.35 +#include <e32std.h>
1.36 +#include <e32std_private.h>
1.37 +#include <e32svr.h>
1.38 +#include <d32comm.h>
1.39 +#include <f32file.h>
1.40 +#include "minkda.h"
1.41 +
1.42 +RNotifier Notifier; // The "UI"
1.43 +RMinKda Trapper;
1.44 +RFs FileSession;
1.45 +TBuf16<KMaxFileName> LogPath; // to specify log file location
1.46 +
1.47 +// Possible outputs where crash information can be dumped
1.48 +enum TOutputType{ EFile, ESerial };
1.49 +
1.50 +// Variables shared between DumpLine() and the various functions used
1.51 +// to format crash info.
1.52 +TOutputType ActiveOutput = EFile;
1.53 +TBool IoError; // ETrue after I/O error
1.54 +RBusDevComm CommPort; // Handle to serial port used
1.55 +RFile File; // Handle to text file used
1.56 +
1.57 +// Maximum length in characters of a line in the file containing
1.58 +// textual information about the crash.
1.59 +const TInt KMaxLineLength = KMaxFullName + 32;
1.60 +
1.61 +class TLexNew : public TLex16
1.62 + {
1.63 +public:
1.64 + inline TLexNew(const TDesC16& aDes) {Assign(aDes);}
1.65 + TInt ExtractParameter(TDes16 &aParam);
1.66 + };
1.67 +
1.68 +TInt TLexNew::ExtractParameter(TDes16 &aParam)
1.69 + {
1.70 + TBuf16<512> token;
1.71 + TBuf16<512> param;
1.72 +
1.73 + TBool GetNext = EFalse;
1.74 +
1.75 + //exit..if it's empty (empty option at the end of command)
1.76 + if (!Peek())
1.77 + return KErrArgument;
1.78 +
1.79 + // remove any space between option and the rest of param..
1.80 + SkipSpace();
1.81 +
1.82 + // just see, what's next..
1.83 + // if there this a param with spaces- should be in "quotes"
1.84 + if (Peek() == '"')
1.85 + {
1.86 + GetNext = ETrue;
1.87 + Inc(); // skip this quote " and move to next position..
1.88 + }
1.89 +
1.90 + // remove spaces after quotes (" param...")
1.91 + SkipSpace();
1.92 +
1.93 + // ..mark next character position as a start of our token
1.94 + Mark();
1.95 +
1.96 + // move until the end of our token (next space)..
1.97 + SkipCharacters();
1.98 +
1.99 + //and get it!!
1.100 + token.Copy(MarkedToken());
1.101 +
1.102 + // if.. there was one-word param.. with quotes..shrink it..and don't try to search next one..
1.103 + if (*(token.MidTPtr(token.Length()-1).Ptr()) == '"')
1.104 + {
1.105 + // just shrink it by that ending quote..
1.106 + token.SetLength(token.Length()-1);
1.107 + GetNext=EFalse;
1.108 + }
1.109 +
1.110 + // This is at least beginning of our param.. let's use it!
1.111 + // add this to beginning of our param..
1.112 + param.Append(token);
1.113 +
1.114 + // if this was param specified in quotes..search for the ending quote..
1.115 + while (GetNext)
1.116 + {
1.117 + // Next is space..
1.118 + SkipSpace();
1.119 +
1.120 + // before taking next one..check it - if '-' on the beginning..
1.121 + // it's either next param specifier..(no ending quote at all)
1.122 + if (Peek() == '-')
1.123 + return KErrArgument;
1.124 +
1.125 + // get the next one..
1.126 + token.Copy(NextToken());
1.127 +
1.128 + // was there any token more? ..if not- we're at the end..
1.129 + // so the ending quote still wasn't found...
1.130 + if (!token.Length())
1.131 + return KErrArgument;
1.132 +
1.133 + // is this the last one - with quote" at the end?
1.134 + if (*(token.MidTPtr(token.Length()-1).Ptr()) == '"')
1.135 + {
1.136 + // just shrink it by that ending quote..
1.137 + token.SetLength(token.Length()-1);
1.138 + GetNext=EFalse;
1.139 + }
1.140 +
1.141 + param.Append(_L(" ")); // there was space in orig. param..restore it..
1.142 + param.Append(token); // and append this token to our param..
1.143 + }
1.144 +
1.145 + // if there was any space at the end..(e.g. if specified: -d"c:\logs ")
1.146 + // - remove it
1.147 + param.TrimRight();
1.148 +
1.149 + //finally - copy param to the referenced descriptor
1.150 + aParam.Copy(param);
1.151 +
1.152 + return KErrNone;
1.153 + }
1.154 +
1.155 +TInt ValidatePath(TDes16 &aLogPath)
1.156 + {
1.157 +
1.158 + // check the length first.. (20 chars for file name..)
1.159 + if (aLogPath.Length() >(KMaxFileName - 20))
1.160 + {
1.161 + Notifier.InfoPrint(_L("directory name too long.."));
1.162 + return KErrArgument;
1.163 + }
1.164 +
1.165 + // if it hasn't drive letter (colon wasn't second..)
1.166 + if (*(aLogPath.MidTPtr(1).Ptr()) != ':')
1.167 + {
1.168 + // if it starts with "\" use system drive..
1.169 + if (*(aLogPath.MidTPtr(0).Ptr()) == '\\')
1.170 + {
1.171 + // if someone specified param like: "\ path\" ...obviously..
1.172 + if (*(aLogPath.MidTPtr(1).Ptr()) == ' ')
1.173 + return KErrArgument;
1.174 +
1.175 + TBuf16<2> drive;
1.176 + drive.Append(RFs::GetSystemDriveChar());
1.177 + drive.LowerCase();
1.178 + drive.Append(_L(":"));
1.179 + aLogPath.Insert(0, drive);
1.180 + }
1.181 + else //otherwise -path not valid..
1.182 + {
1.183 + return KErrArgument;
1.184 + }
1.185 + }
1.186 +
1.187 + // and add backslash if needed
1.188 + if (*(aLogPath.MidTPtr(aLogPath.Length()-1).Ptr()) != '\\')
1.189 + aLogPath.Append(_L("\\"));
1.190 +
1.191 + //open file session..
1.192 + if (FileSession.Connect() != KErrNone)
1.193 + return KErrGeneral;
1.194 +
1.195 + RDir dir;
1.196 + TInt err=KErrNone;
1.197 + if (dir.Open(FileSession, aLogPath, KEntryAttMatchExclusive) != KErrNone)
1.198 + {
1.199 + Notifier.InfoPrint(_L("specified directory doesn't exist"));
1.200 + LogPath.Zero(); //clear global path..
1.201 + err = KErrArgument;
1.202 + }
1.203 + else
1.204 + {
1.205 + dir.Close();
1.206 + }
1.207 +
1.208 + // close file session..
1.209 + FileSession.Close();
1.210 +
1.211 + return err;
1.212 + }
1.213 +
1.214 +
1.215 +// Open specified serial port and push handle on the cleanup stack.
1.216 +
1.217 +void OpenCommPortLC(TInt aPortNum)
1.218 + {
1.219 +#ifdef __WINS__
1.220 + _LIT(KPdd, "ECDRV");
1.221 +#else
1.222 + _LIT(KPdd, "EUART");
1.223 +#endif
1.224 + _LIT(KLdd, "ECOMM");
1.225 + _LIT(KErrPdd, "Failed to load serial PDD");
1.226 + _LIT(KErrLdd, "Failed to load serial LDD");
1.227 + _LIT(KErrOpen, "Failed to open comm port");
1.228 + _LIT(KErrCfg, "Failed to configure comm port");
1.229 +
1.230 + TInt r = User::LoadPhysicalDevice(KPdd);
1.231 + if (r != KErrNone && r != KErrAlreadyExists)
1.232 + {
1.233 + Notifier.InfoPrint(KErrPdd);
1.234 + User::Leave(r);
1.235 + }
1.236 +
1.237 + r = User::LoadLogicalDevice(KLdd);
1.238 + if (r != KErrNone && r != KErrAlreadyExists)
1.239 + {
1.240 + Notifier.InfoPrint(KErrLdd);
1.241 + User::Leave(r);
1.242 + }
1.243 +
1.244 + r = CommPort.Open(aPortNum);
1.245 + if (r != KErrNone)
1.246 + {
1.247 + Notifier.InfoPrint(KErrOpen);
1.248 + User::Leave(r);
1.249 + }
1.250 + CleanupClosePushL(CommPort);
1.251 +
1.252 + TCommConfig cfgBuf;
1.253 + TCommConfigV01& cfg=cfgBuf();
1.254 + CommPort.Config(cfgBuf);
1.255 + cfg.iRate=EBps115200;
1.256 + cfg.iDataBits=EData8;
1.257 + cfg.iStopBits=EStop1;
1.258 + cfg.iParity=EParityNone;
1.259 + cfg.iHandshake=KConfigObeyXoff|KConfigSendXoff;
1.260 + cfg.iFifo=EFifoEnable;
1.261 + cfg.iTerminatorCount=0;
1.262 + cfg.iSIREnable=ESIRDisable;
1.263 + r = CommPort.SetConfig(cfgBuf);
1.264 + if (r != KErrNone)
1.265 + {
1.266 + Notifier.InfoPrint(KErrCfg);
1.267 + User::Leave(r);
1.268 + }
1.269 + }
1.270 +
1.271 +
1.272 +void ParseCmdLineL(TInt& aPortNum, TInt& aMaxTrapCount, TBool& aInteractive, TBool& aDumpStack)
1.273 + {
1.274 + _LIT(KInvalidArg, "Invalid command-line");
1.275 +
1.276 + HBufC* cl = HBufC::NewLC(User::CommandLineLength());
1.277 + TPtr clp = cl->Des();
1.278 + User::CommandLine(clp);
1.279 +
1.280 + // If started from UIKON shell, ignore command-line and use defaults
1.281 + if (clp.Match(_L("?:\\*")) == 0)
1.282 + return;
1.283 +
1.284 + TLexNew lex(*cl);
1.285 +
1.286 + while (! lex.Eos())
1.287 + {
1.288 + TInt r = KErrArgument;
1.289 + if (lex.Get() == '-')
1.290 + {
1.291 + switch (lex.Get())
1.292 + {
1.293 + case 'n':
1.294 + r = lex.Val(aMaxTrapCount);
1.295 + break;
1.296 + case 'p':
1.297 + r = lex.Val(aPortNum);
1.298 + if (r == KErrNone)
1.299 + ActiveOutput = ESerial;
1.300 + break;
1.301 + case 'b':
1.302 + aInteractive = EFalse;
1.303 + r = KErrNone;
1.304 + break;
1.305 + case 'm':
1.306 + aDumpStack = EFalse;
1.307 + r = KErrNone;
1.308 + break;
1.309 + case 'd':
1.310 + //try to extract path and store it in global buffer
1.311 + r = lex.ExtractParameter(LogPath);
1.312 + // check, if specified path is valid
1.313 + if (r == KErrNone)
1.314 + r = ValidatePath(LogPath);
1.315 + break;
1.316 + }
1.317 + }
1.318 + if (r != KErrNone)
1.319 + {
1.320 + Notifier.InfoPrint(KInvalidArg);
1.321 + User::Leave(KErrArgument);
1.322 + }
1.323 + lex.SkipSpace();
1.324 + }
1.325 +
1.326 + CleanupStack::PopAndDestroy(cl);
1.327 + }
1.328 +
1.329 +
1.330 +// Dump specified line + CRLF on the selected output. Set IoError to
1.331 +// ETrue if an error occurs.
1.332 +
1.333 +void DumpLine(TDes8& aLine)
1.334 + {
1.335 + TInt r;
1.336 + _LIT8(KCrLf, "\r\n");
1.337 + aLine.Append(KCrLf);
1.338 + if (ActiveOutput == ESerial)
1.339 + {
1.340 + TRequestStatus s;
1.341 + CommPort.Write(s, aLine);
1.342 + User::WaitForRequest(s);
1.343 + r = s.Int();
1.344 + }
1.345 + else
1.346 + r = File.Write(aLine);
1.347 + if (r != KErrNone)
1.348 + IoError = ETrue;
1.349 + }
1.350 +
1.351 +
1.352 +void DumpExcInfo(const TDbgCpuExcInfo& aInfo, TDes8& aLine)
1.353 + {
1.354 + _LIT8(KHdr, "\r\nUNHANDLED EXCEPTION:");
1.355 + aLine = KHdr;
1.356 + DumpLine(aLine);
1.357 +#ifdef __MARM__
1.358 + _LIT8(KFmt1, "code=%d PC=%08x FAR=%08x FSR=%08x");
1.359 + aLine.Format(KFmt1, aInfo.iExcCode, aInfo.iFaultPc, aInfo.iFaultAddress, aInfo.iFaultStatus);
1.360 + DumpLine(aLine);
1.361 + _LIT8(KFmt2, "R13svc=%08x R14svc=%08x SPSRsvc=%08x");
1.362 + aLine.Format(KFmt2, aInfo.iR13Svc, aInfo.iR14Svc, aInfo.iSpsrSvc);
1.363 + DumpLine(aLine);
1.364 +#else
1.365 + (void) aInfo; // silence warning
1.366 +#endif
1.367 + }
1.368 +
1.369 +
1.370 +void DumpRegisters(const TDbgRegSet& aRegs, TDes8& aLine)
1.371 + {
1.372 +#if defined(__MARM__)
1.373 + _LIT8(KHdr, "\r\nUSER REGISTERS:");
1.374 + aLine = KHdr;
1.375 + DumpLine(aLine);
1.376 + _LIT8(KFmtCpsr, "CPSR=%08x");
1.377 + aLine.Format(KFmtCpsr, aRegs.iCpsr);
1.378 + DumpLine(aLine);
1.379 + for (TInt i=0; i<TDbgRegSet::KRegCount; i+=4)
1.380 + {
1.381 + _LIT8(KFmtReg, "r%02d=%08x %08x %08x %08x");
1.382 + aLine.Format(KFmtReg, i, aRegs.iRn[i], aRegs.iRn[i+1], aRegs.iRn[i+2], aRegs.iRn[i+3]);
1.383 + DumpLine(aLine);
1.384 + }
1.385 +#else
1.386 + (void) aRegs; // silence warnings
1.387 + (void) aLine;
1.388 +#endif
1.389 + }
1.390 +
1.391 +
1.392 +void DumpCodeSegs(TUint aPid, TDes8& aLine)
1.393 + {
1.394 + _LIT(KPanicCodeMods, "DEXC-CODEMOD");
1.395 + _LIT8(KHdr, "\r\nCODE SEGMENTS:");
1.396 + _LIT8(KFmtOverflow, "Only first %d code modules displayed");
1.397 + _LIT8(KFmtMod, "%08X-%08X %S");
1.398 +
1.399 + aLine = KHdr;
1.400 + DumpLine(aLine);
1.401 +
1.402 + // :FIXME: improve API
1.403 + // :FIXME: suspend/resume all threads in process
1.404 + const TInt KMaxCount = 128;
1.405 + TAny* handles[KMaxCount];
1.406 + TInt c = KMaxCount;
1.407 +
1.408 + TInt r = Trapper.GetCodeSegs(aPid, handles, c);
1.409 + __ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicCodeMods, r));
1.410 +
1.411 + if (c > KMaxCount)
1.412 + {
1.413 + aLine.Format(KFmtOverflow, c);
1.414 + DumpLine(aLine);
1.415 + c = KMaxCount;
1.416 + }
1.417 +
1.418 + for (TInt i=0; i<c; i++)
1.419 + {
1.420 + TDbgCodeSegInfo info;
1.421 + r = Trapper.GetCodeSegInfo(handles[i], aPid, info);
1.422 + if (r == KErrNone)
1.423 + {
1.424 + TBuf8<KMaxFileName> path;
1.425 + path.Copy(info.iPath);
1.426 + aLine.Format(KFmtMod, info.iCodeBase, info.iCodeBase+info.iCodeSize, &path);
1.427 + DumpLine(aLine);
1.428 + }
1.429 + }
1.430 + }
1.431 +
1.432 +
1.433 +void DumpTextInfo(const TDbgCrashInfo& aCrashInfo, const TDbgThreadInfo& aThreadInfo)
1.434 + {
1.435 + _LIT(KFmtTextFile, "d_exc_%d.txt");
1.436 + _LIT(KErrTextOpen, "text file open error");
1.437 + _LIT(KErrTextWrite, "text file write error");
1.438 +
1.439 + if (ActiveOutput == EFile)
1.440 + {
1.441 + TBuf16<KMaxFileName> name;
1.442 + name.Format(KFmtTextFile, aCrashInfo.iTid);
1.443 +
1.444 + // if -d param wasn't specified, use default location..(root dir on system drive)
1.445 + if(!LogPath.Length())
1.446 + {
1.447 + LogPath.Append(RFs::GetSystemDriveChar());
1.448 + LogPath.LowerCase();
1.449 + LogPath.Append(_L(":\\"));
1.450 + }
1.451 +
1.452 + TBuf16<KMaxFileName> filename;
1.453 + filename.Copy(LogPath);
1.454 + filename.Append(name);
1.455 +
1.456 + TInt r = File.Replace(FileSession, filename, EFileWrite+EFileShareAny+EFileStream);
1.457 + if (r != KErrNone)
1.458 + {
1.459 + Notifier.InfoPrint(KErrTextOpen);
1.460 + return;
1.461 + }
1.462 + }
1.463 +
1.464 + IoError = EFalse;
1.465 +
1.466 + // Note that following buffer is passed to callee functions and
1.467 + // reuse to minimise stack footprint.
1.468 + TBuf8<KMaxLineLength> line;
1.469 +
1.470 + line.Fill('-', 76);
1.471 + DumpLine(line);
1.472 + _LIT8(KHdr, "EKA2 USER CRASH LOG");
1.473 + line = KHdr;
1.474 + DumpLine(line);
1.475 + line.Copy(aThreadInfo.iFullName);
1.476 + _LIT8(KName, "Thread Name: ");
1.477 + line.Insert(0, KName);
1.478 + DumpLine(line);
1.479 + _LIT8(KFmtTid, "Thread ID: %u");
1.480 + line.Format(KFmtTid, aCrashInfo.iTid);
1.481 + DumpLine(line);
1.482 + _LIT8(KFmtStack, "User Stack %08X-%08X");
1.483 + line.Format(KFmtStack, aThreadInfo.iStackBase,
1.484 + aThreadInfo.iStackBase+aThreadInfo.iStackSize);
1.485 + DumpLine(line);
1.486 +
1.487 + if (aCrashInfo.iType == TDbgCrashInfo::EPanic)
1.488 + {
1.489 + TBuf8<KMaxExitCategoryName> cat;
1.490 + cat.Copy(aThreadInfo.iExitCategory);
1.491 + _LIT8(KFmtPanic, "Panic: %S-%d");
1.492 + line.Format(KFmtPanic, &cat, aThreadInfo.iExitReason);
1.493 + DumpLine(line);
1.494 + }
1.495 + else
1.496 + DumpExcInfo(aCrashInfo.iCpu, line);
1.497 +
1.498 + DumpRegisters(aThreadInfo.iCpu, line);
1.499 + DumpCodeSegs(aThreadInfo.iPid, line);
1.500 +
1.501 + line.Zero();
1.502 + DumpLine(line);
1.503 +
1.504 + if (IoError)
1.505 + Notifier.InfoPrint(KErrTextWrite);
1.506 +
1.507 + if (ActiveOutput == EFile)
1.508 + File.Close();
1.509 + }
1.510 +
1.511 +
1.512 +// Output stack on selected output. If serial port, use
1.513 +// human-readable format. If file, use binary format.
1.514 +
1.515 +void DumpStack(TUint aTid, const TDbgThreadInfo& aInfo)
1.516 + {
1.517 + _LIT(KFmtStackFile, "d_exc_%d.stk");
1.518 + _LIT(KErrStackOpen, "stack file open error");
1.519 + _LIT(KErrStackWrite, "stack file write error");
1.520 + _LIT(KPanicReadStack, "DEXC-READSTACK");
1.521 +
1.522 + TInt r;
1.523 + IoError = EFalse;
1.524 +
1.525 + RFile file;
1.526 + if (ActiveOutput == EFile)
1.527 + {
1.528 + TBuf16<KMaxFileName> name;
1.529 + name.Format(KFmtStackFile, aTid);
1.530 +
1.531 + // if -d param wasn't specified, use default location..(root dir on system drive)
1.532 + if(!LogPath.Length())
1.533 + {
1.534 + LogPath.Append(RFs::GetSystemDriveChar());
1.535 + LogPath.LowerCase();
1.536 + LogPath.Append(_L(":\\"));
1.537 + }
1.538 +
1.539 + TBuf16<KMaxFileName> filename;
1.540 + filename.Copy(LogPath);
1.541 + filename.Append(name);
1.542 +
1.543 + r = file.Replace(FileSession, filename, EFileWrite+EFileShareAny+EFileStream);
1.544 + if (r != KErrNone)
1.545 + {
1.546 + Notifier.InfoPrint(KErrStackOpen);
1.547 + return;
1.548 + }
1.549 + }
1.550 +
1.551 + const TInt KBufSize = 256;
1.552 + TBuf8<KBufSize> buf;
1.553 + TLinAddr top = aInfo.iStackBase + aInfo.iStackSize;
1.554 + for (TLinAddr base = aInfo.iStackBase; base < top; base += KBufSize)
1.555 + {
1.556 + // Read chunk of stack. Should always succeeds as thread has
1.557 + // been suspended by LDD.
1.558 + r = Trapper.ReadMem(aTid, base, buf);
1.559 + __ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicReadStack, r));
1.560 +
1.561 + if (ActiveOutput == ESerial)
1.562 + {
1.563 + TBuf8<80> out;
1.564 + TBuf8<20> ascii;
1.565 + TUint a = base;
1.566 + TInt len = buf.Length();
1.567 + TInt offset = 0;
1.568 + while(len>0)
1.569 + {
1.570 + out.Zero();
1.571 + ascii.Zero();
1.572 + out.AppendNumFixedWidth(a,EHex,8);
1.573 + out.Append(_L8(": "));
1.574 + TUint b;
1.575 + for (b=0; b<16; b++)
1.576 + {
1.577 + TUint8 c=*(buf.Ptr()+offset+b);
1.578 + out.AppendNumFixedWidth(c,EHex,2);
1.579 + out.Append(' ');
1.580 + if (c<0x20 || c>=0x7f)
1.581 + c=0x2e;
1.582 + ascii.Append(TChar(c));
1.583 + }
1.584 + out.Append(ascii);
1.585 + DumpLine(out);
1.586 + a+=16;
1.587 + offset += 16;
1.588 + len-=16;
1.589 + }
1.590 + }
1.591 + else
1.592 + {
1.593 + if (file.Write(buf) != KErrNone)
1.594 + IoError = ETrue;
1.595 + }
1.596 + }
1.597 +
1.598 + if (IoError)
1.599 + Notifier.InfoPrint(KErrStackWrite);
1.600 + if (ActiveOutput == EFile)
1.601 + file.Close();
1.602 + }
1.603 +
1.604 +
1.605 +// Display a dialog box containing basic facts about the crash and ask
1.606 +// the user whether to dump detailed information or skip this crash.
1.607 +
1.608 +enum TDebugChoice { EDoDebug, EDoNotDebug };
1.609 +
1.610 +TDebugChoice CrashDialog(TDbgCrashInfo::TType aCrashType, const TDbgThreadInfo& aInfo)
1.611 + {
1.612 + _LIT(KExc, "Exception");
1.613 + _LIT(KPanic, "Panic %S:%d");
1.614 + _LIT(KBut1, "Do Not Debug");
1.615 + _LIT(KBut2, "Debug");
1.616 +
1.617 + TBuf<64> line1;
1.618 + if (aCrashType == TDbgCrashInfo::EException)
1.619 + line1 = KExc;
1.620 + else
1.621 + line1.Format(KPanic, &aInfo.iExitCategory, aInfo.iExitReason);
1.622 + TInt r;
1.623 + TRequestStatus s;
1.624 + Notifier.Notify(line1, aInfo.iFullName, KBut1, KBut2, r, s);
1.625 + User::WaitForRequest(s);
1.626 + return r == 0 ? EDoNotDebug : EDoDebug;
1.627 + }
1.628 +
1.629 +
1.630 +void MainL()
1.631 + {
1.632 + _LIT(KErrFs, "Failed to connect to file server");
1.633 + _LIT(KErrLoadLdd, "Failed to load KDA LDD");
1.634 + _LIT(KErrOpenLdd, "Failed to open KDA LDD");
1.635 + _LIT(KLddPath, "MINKDA");
1.636 + _LIT(KStarted, "D_EXC started");
1.637 + _LIT(KCrash, "Crash detected");
1.638 + _LIT(KPanicThreadInfo, "DEXC-THREADINFO");
1.639 +
1.640 + TInt portNum;
1.641 + TInt maxTrapCount = -1;
1.642 + TBool isInteractive = ETrue;
1.643 + TBool dumpStack = ETrue;
1.644 + ParseCmdLineL(portNum, maxTrapCount, isInteractive, dumpStack);
1.645 +
1.646 + // Open selected output and push resulting handle on cleanup
1.647 + // stack.
1.648 + TInt r;
1.649 + if (ActiveOutput == EFile)
1.650 + {
1.651 + if ((r = FileSession.Connect()) != KErrNone)
1.652 + {
1.653 + Notifier.InfoPrint(KErrFs);
1.654 + User::Leave(r);
1.655 + }
1.656 + CleanupClosePushL(FileSession);
1.657 + }
1.658 + else
1.659 + OpenCommPortLC(portNum);
1.660 +
1.661 + r = User::LoadLogicalDevice(KLddPath);
1.662 + if (r != KErrNone && r != KErrAlreadyExists)
1.663 + {
1.664 + Notifier.InfoPrint(KErrLoadLdd);
1.665 + User::Leave(r);
1.666 + }
1.667 +
1.668 +// See comment near __KHEAP_MARKEND
1.669 +// __KHEAP_MARK;
1.670 +
1.671 + r = Trapper.Open();
1.672 + if (r != KErrNone)
1.673 + {
1.674 + Notifier.InfoPrint(KErrOpenLdd);
1.675 + User::Leave(r);
1.676 + }
1.677 + CleanupClosePushL(Trapper);
1.678 +
1.679 + Notifier.InfoPrint(KStarted);
1.680 +
1.681 + // Main loop
1.682 + TRequestStatus s;
1.683 + TDbgCrashInfo crashInfo;
1.684 + Trapper.Trap(s, crashInfo);
1.685 + for (TInt crashCount = 0; maxTrapCount<0 || crashCount<maxTrapCount; ++crashCount)
1.686 + {
1.687 + User::WaitForRequest(s);
1.688 +
1.689 + // Get more info about crashed thread. Should always succeeds
1.690 + // as the thread has been suspended by LDD.
1.691 + TDbgThreadInfo threadInfo;
1.692 + TInt r = Trapper.GetThreadInfo(crashInfo.iTid, threadInfo);
1.693 + __ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicThreadInfo, r));
1.694 +
1.695 + if (! isInteractive)
1.696 + Notifier.InfoPrint(KCrash);
1.697 + if (! isInteractive || CrashDialog(crashInfo.iType, threadInfo) == EDoDebug)
1.698 + {
1.699 + DumpTextInfo(crashInfo, threadInfo);
1.700 + if (dumpStack)
1.701 + DumpStack(crashInfo.iTid, threadInfo);
1.702 + }
1.703 + Trapper.Trap(s, crashInfo);
1.704 + Trapper.KillCrashedThread();
1.705 + }
1.706 +
1.707 + Trapper.CancelTrap();
1.708 +
1.709 + CleanupStack::PopAndDestroy(&Trapper);
1.710 + CleanupStack::PopAndDestroy(); // FileSession or CommPort
1.711 +
1.712 +// Commented out because the InfoPrint thread may or may not have
1.713 +// terminated when we reach this point. It if hasn't a spurious
1.714 +// memory leak will be reported.
1.715 +// #ifdef _DEBUG
1.716 +// User::After(3000000);
1.717 +// __KHEAP_MARKEND;
1.718 +// #endif
1.719 +
1.720 + User::FreeLogicalDevice(KKdaLddName);
1.721 + }
1.722 +
1.723 +
1.724 +TInt E32Main()
1.725 + {
1.726 + _LIT(KPanicNtf, "DEXC-NO-NTF");
1.727 + _LIT(KPanicLeave, "DEXC-LEAVE");
1.728 + _LIT(KPanicOom, "DEXC-NO-CLEANUP");
1.729 +
1.730 + // :FIXME: remove when platform security is always on
1.731 + RProcess().DataCaging(RProcess::EDataCagingOn);
1.732 +
1.733 +#ifdef _DEBUG
1.734 + TInt phcStart;
1.735 + TInt thcStart;
1.736 + RThread().HandleCount(phcStart, thcStart);
1.737 +#endif
1.738 +
1.739 + TInt r = Notifier.Connect();
1.740 + __ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicNtf, r));
1.741 +
1.742 + __UHEAP_MARK;
1.743 + CTrapCleanup* cleanup = CTrapCleanup::New();
1.744 + __ASSERT_ALWAYS(cleanup, User::Panic(KPanicOom, KErrNoMemory));
1.745 + TRAP(r, MainL());
1.746 + __ASSERT_ALWAYS(r == KErrNone, User::Panic(KPanicLeave, r));
1.747 + delete cleanup;
1.748 + __UHEAP_MARKEND;
1.749 +
1.750 + Notifier.Close();
1.751 +
1.752 +#ifdef _DEBUG
1.753 + TInt phcEnd;
1.754 + TInt thcEnd;
1.755 + RThread().HandleCount(phcEnd, thcEnd);
1.756 + __ASSERT_DEBUG(phcStart == phcEnd, User::Panic(_L("DEXC-PHC"), phcEnd-phcStart));
1.757 + __ASSERT_DEBUG(thcStart == thcEnd, User::Panic(_L("DEXC-THC"), thcEnd-thcStart));
1.758 +#endif
1.759 +
1.760 + return r;
1.761 + }