|
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 } |