|
1 // fdb.cpp |
|
2 // |
|
3 // Copyright (c) 2009 - 2010 Accenture. All rights reserved. |
|
4 // This component and the accompanying materials are made available |
|
5 // under the terms of the "Eclipse Public License v1.0" |
|
6 // which accompanies this distribution, and is available |
|
7 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 // |
|
9 // Initial Contributors: |
|
10 // Accenture - Initial contribution |
|
11 // |
|
12 |
|
13 #include <fshell/ioutils.h> |
|
14 #include <fshell/memoryaccesscmd.h> |
|
15 #include <fshell/ltkutils.h> |
|
16 #include <fshell/bsym.h> |
|
17 #include <fshell/descriptorutils.h> |
|
18 #include <fshell/iocons_writer.h> |
|
19 #include <fshell/line_editor.h> |
|
20 #include <fshell/qr3dll.h> |
|
21 #include <u32std.h> // Is this available in all environments? If not, need to define TDesType ourselves |
|
22 #include <babitflags.h> |
|
23 #include <badesca.h> |
|
24 _LIT(KCrLf, "\r\n"); |
|
25 _LIT(KPrompt, "fdb>"); |
|
26 |
|
27 using namespace IoUtils; |
|
28 using namespace LtkUtils; |
|
29 |
|
30 const TInt KMaxRegisters = 32; |
|
31 |
|
32 class CCmdFdb : public CMemoryAccessCommandBase, public MLineEditorObserver, public MLineCompleter |
|
33 { |
|
34 public: |
|
35 static CCommandBase* NewLC(); |
|
36 ~CCmdFdb(); |
|
37 private: |
|
38 struct SThreadContext; |
|
39 CCmdFdb(); |
|
40 void InstallL(); |
|
41 void AttachL(TUint aThreadId); |
|
42 void Detach(SThreadContext* aContext); |
|
43 void PrintThreadInfo(SThreadContext& aContext); |
|
44 void PrintRegistersL(SThreadContext& aThread); |
|
45 void PrintRegistersL(SThreadContext& aThread, TBool aUserMode); |
|
46 void PrintRegistersLRPC(SThreadContext& aThread, TBool aUserMode); |
|
47 void RefreshL(SThreadContext& aContext); |
|
48 void RefreshIfRunningL(SThreadContext& aContext); |
|
49 TPtrC LookupSymbol(TUint32 aAddress); |
|
50 void StartCommandParsingL(); |
|
51 void ProcessLineL(const TDesC& aLine); |
|
52 void FocusL(TUint aThreadId); |
|
53 SThreadContext* ContextForThread(TUint aThreadId) const; |
|
54 SThreadContext& CurrentL(); |
|
55 SThreadContext& CurrentNoRefreshL(); |
|
56 enum TPrintMode { EJustSymbols, EAllData, EHexDump }; |
|
57 void PrintMemL(TUint aThreadId, TUint32 aStart, TUint32 aEnd, TPrintMode aMode); |
|
58 void BrrrrrainsL(); |
|
59 void ShowHelpTextL(); |
|
60 TBool IsSymbol(TUint aAddress) const; |
|
61 void StartInteractiveViewL(TLinAddr aAddress); |
|
62 void DrawMemViewL(TBool aUpdate); |
|
63 void UpdateMemForMemViewL(); |
|
64 static TInt IsDescriptorHeader(TUint8 const* aHeaderPtr, TInt aMaxLen); |
|
65 void ShowBreakpointsL(); |
|
66 static TInt BreakTest(TAny* aPtr); |
|
67 static TInt BreakTestLtk(TAny* aPtr); |
|
68 static TInt BreakTestCond(TAny* aPtr); |
|
69 void ClearAllBreakpointsL(); |
|
70 void BreakpointHit(const RMemoryAccess::TBreakpointNotification& aNotif); |
|
71 void CompleteLineWithSymbolL(TConsoleLine& aLine, TLex& aLex); |
|
72 void CheckForConditionL(TLex& aLex, RMemoryAccess::TPredicate& aCondition); |
|
73 |
|
74 private: // From MLineCompleter |
|
75 void LcCompleteLineL(TConsoleLine& aLine, const TChar& aEscapeChar); |
|
76 |
|
77 private: // From MLineEditorObserver |
|
78 void LeoHandleLine(const TDesC& aLine); |
|
79 |
|
80 private: // From CCommandBase. |
|
81 virtual const TDesC& Name() const; |
|
82 virtual void DoRunL(); |
|
83 virtual void ArgumentsL(RCommandArgumentList& aArguments); |
|
84 virtual void OptionsL(RCommandOptionList& aOptions); |
|
85 void RunL(); |
|
86 void DoCancel(); |
|
87 |
|
88 private: |
|
89 // Command line stuff |
|
90 TBool iAllThreads; |
|
91 TUint iThreadId; |
|
92 TFileName2 iBsymFile; |
|
93 TFileName2 iMapFileDir; |
|
94 |
|
95 // Thread context |
|
96 struct SThreadContext |
|
97 { |
|
98 SThreadContext() : iUserValidRegisters(0), iSupervisorValidRegisters(0), iFlags(0) {} |
|
99 |
|
100 RThread iThread; |
|
101 TUint32 iUserRegisters[KMaxRegisters]; |
|
102 TUint32 iUserValidRegisters; |
|
103 TUint32 iSupervisorRegisters[KMaxRegisters]; |
|
104 TUint32 iSupervisorValidRegisters; |
|
105 TThreadKernelInfo iKernelInfo; |
|
106 enum TFlags { ERunning = 1, }; |
|
107 TBitFlags32 iFlags; |
|
108 }; |
|
109 SThreadContext* iCurrent; |
|
110 RPointerArray<SThreadContext> iThreads; |
|
111 |
|
112 // Other stuff |
|
113 CSymbolics* iSymbols; |
|
114 TBuf<256> iTempNameBuf; |
|
115 |
|
116 // Line editor support |
|
117 TIoConsWriterAdaptor iConsoleAdapter; |
|
118 CLineEditor* iLineEditor; |
|
119 TBuf<256> iCommandLineBuf; |
|
120 TBool iFinishedCommand; |
|
121 TBool iShouldExit; |
|
122 |
|
123 // Interactive wossname support |
|
124 enum TViewType |
|
125 { |
|
126 EUnspecified, |
|
127 EStack, |
|
128 EHeap, |
|
129 }; |
|
130 TViewType iMemoryViewType; |
|
131 TLinAddr iMemStart; |
|
132 RBuf8 iMemBuf; |
|
133 /* |
|
134 class TCrumb |
|
135 { |
|
136 enum TType { EAddress, EHeapCell, }; |
|
137 TType iType; |
|
138 TLinAddr iAddress; |
|
139 TInt iIndex; |
|
140 } |
|
141 RArray<TCrumb> iBreadcrumbs; |
|
142 RArray<TLinAddr> iMarks; |
|
143 RArray<TLinAddr> iLinks; |
|
144 */ |
|
145 |
|
146 // Breakpoint support |
|
147 RMemoryAccess::TBreakpointNotification iBreakpointNotification; |
|
148 TPckg<RMemoryAccess::TBreakpointNotification> iBreakpointNotificationPkg; |
|
149 class CBreakpointNotifier : public CActive |
|
150 { |
|
151 public: |
|
152 CBreakpointNotifier(CCmdFdb& aCmd); |
|
153 ~CBreakpointNotifier(); |
|
154 |
|
155 private: |
|
156 void Request(); |
|
157 void RunL(); |
|
158 void DoCancel(); |
|
159 |
|
160 private: |
|
161 CCmdFdb& iCmd; |
|
162 }; |
|
163 friend class CBreakpointNotifier; // Declaration needed for gcc 2.9 |
|
164 CBreakpointNotifier* iBreakpointNotifier; |
|
165 }; |
|
166 |
|
167 EXE_BOILER_PLATE(CCmdFdb) |
|
168 |
|
169 CCommandBase* CCmdFdb::NewLC() |
|
170 { |
|
171 CCmdFdb* self = new(ELeave) CCmdFdb(); |
|
172 CleanupStack::PushL(self); |
|
173 self->BaseConstructL(); |
|
174 return self; |
|
175 } |
|
176 |
|
177 CCmdFdb::~CCmdFdb() |
|
178 { |
|
179 Cancel(); |
|
180 delete iLineEditor; |
|
181 for (TInt i = 0; i < iThreads.Count(); i++) |
|
182 { |
|
183 SThreadContext* thread = iThreads[i]; |
|
184 thread->iThread.Close(); |
|
185 delete thread; |
|
186 } |
|
187 iThreads.Close(); |
|
188 delete iSymbols; |
|
189 iMemBuf.Close(); |
|
190 delete iBreakpointNotifier; |
|
191 /* |
|
192 iLinks.Close(); |
|
193 iBreadcrumbs.Close(); |
|
194 iMarks.Close(); |
|
195 */ |
|
196 } |
|
197 |
|
198 CCmdFdb::CCmdFdb() |
|
199 : CMemoryAccessCommandBase(EManualComplete), iConsoleAdapter(Stdout()), iBreakpointNotificationPkg(iBreakpointNotification) |
|
200 { |
|
201 } |
|
202 |
|
203 const TDesC& CCmdFdb::Name() const |
|
204 { |
|
205 _LIT(KName, "fdb"); |
|
206 return KName; |
|
207 } |
|
208 |
|
209 void CCmdFdb::ArgumentsL(RCommandArgumentList& aArguments) |
|
210 { |
|
211 aArguments.AppendUintL(iThreadId, _L("threadid")); |
|
212 } |
|
213 |
|
214 void CCmdFdb::OptionsL(RCommandOptionList& aOptions) |
|
215 { |
|
216 aOptions.AppendBoolL(iAllThreads, _L("all")); |
|
217 aOptions.AppendFileNameL(iBsymFile, _L("symbols")); |
|
218 aOptions.AppendFileNameL(iMapFileDir, _L("mapfiles")); |
|
219 } |
|
220 |
|
221 struct STestData |
|
222 { |
|
223 STestData() : iPtr8(NULL, 0) {} |
|
224 |
|
225 char iPad1[12]; |
|
226 TBuf8<6> iBuf8; |
|
227 TInt iPad2; |
|
228 TBufC8<9> iBufC8; |
|
229 char iPad3; |
|
230 TPtrC8 iPtrC8; |
|
231 TUint64 iPad4; |
|
232 TPtr8 iPtr8; |
|
233 char iPad5[3]; |
|
234 }; |
|
235 |
|
236 |
|
237 void CCmdFdb::DoRunL() |
|
238 { |
|
239 LoadMemoryAccessL(); |
|
240 TInt mode = iMemAccess.GetZombieDebugMode(); |
|
241 LeaveIfErr(mode, _L("Couldn't get zombie mode from driver")); |
|
242 |
|
243 iSymbols = new(ELeave) CSymbolics(FsL()); |
|
244 if (iBsymFile.Length()) |
|
245 { |
|
246 //Printf(_L("Loading symbolics... ")); // It can take a while... |
|
247 iSymbols->AddBsymFileL(iBsymFile); |
|
248 //Printf(_L("done.\r\n")); |
|
249 } |
|
250 if (iMapFileDir.Length()) |
|
251 { |
|
252 iSymbols->SetFallbackMapFileDirL(iMapFileDir); |
|
253 } |
|
254 |
|
255 // Testing |
|
256 //CMapFile* map = CMapFile::NewL(FsL(), _L("C:\\symbols\\QResources3.exe.map")); |
|
257 //iSymbols->AddMapFileL(map); |
|
258 //const TDesC& symName = iSymbols->LookupL(_L("QResources3.exe"), 0x10abc); |
|
259 //Printf(_L("Found symbol = %S\r\n"), &symName); |
|
260 |
|
261 if (Env().IsDefined(_L("FDB_TEST"))) |
|
262 { |
|
263 STestData test; |
|
264 test.iBuf8 = _L8("12345"); |
|
265 test.iBufC8 = _L8("abcdefg"); |
|
266 test.iPtrC8.Set(test.iBufC8); |
|
267 TPckg<STestData> pkg(test); |
|
268 iMemBuf.Assign(pkg.AllocL()); |
|
269 DrawMemViewL(EFalse); |
|
270 return; |
|
271 } |
|
272 |
|
273 TBool install = ETrue; |
|
274 // Figure out something sensible to do |
|
275 if (mode != 0 || iThreadId != 0) install = EFalse; |
|
276 |
|
277 if (iAllThreads) Printf(_L("Note that because --all-threads was specified, this and all future commands will hang rather than exiting!\r\n")); |
|
278 |
|
279 InstallL(); // Always do this, in case user has changed the --all-threads setting |
|
280 if (install) |
|
281 { |
|
282 // This will really mess up evalid, but the toolkit doesn't really use it so I don't feel *too* guilty |
|
283 Printf(_L8("Welcome to fdb (build " __DATE__ " " __TIME__ ").\r\nDebugger hook installed and will stay running until you type 'uninstall'.\r\nYou can exit fdb if required and it will stay running in the background.\r\nType 'help' for command info.\r\n")); |
|
284 } |
|
285 else |
|
286 { |
|
287 AttachL(iThreadId); |
|
288 } |
|
289 |
|
290 iBreakpointNotifier = new(ELeave) CBreakpointNotifier(*this); |
|
291 TInt err = iMemAccess.RegisterPersistantBreakpoint(LtkUtils::BreakpointAddr()); |
|
292 if (err != KErrNone && err != KErrAlreadyExists) |
|
293 { |
|
294 LeaveIfErr(err, _L("Couldn't install the LtkUtils::Breakpoint() breakpoint")); |
|
295 } |
|
296 StartCommandParsingL(); |
|
297 } |
|
298 |
|
299 void CCmdFdb::InstallL() |
|
300 { |
|
301 LeaveIfErr(iMemAccess.SetZombieDebugMode(iAllThreads ? 2 : 1), _L("Couldn't install thread monitor")); |
|
302 const TUint32 KAllThreadsSystem = 1; // Word 2, bit 0 |
|
303 TUint32 dbgmask = UserSvr::DebugMask(2); |
|
304 if (dbgmask & KAllThreadsSystem) |
|
305 { |
|
306 Printf(_L("(Clearing KAllThreadsSystem attribute)\r\n")); |
|
307 User::SetDebugMask(dbgmask & ~KAllThreadsSystem, 2); |
|
308 } |
|
309 } |
|
310 |
|
311 void CCmdFdb::AttachL(TUint aThreadId) |
|
312 { |
|
313 if (aThreadId == 0) |
|
314 { |
|
315 // Need to figure out something to attach to |
|
316 TBuf8<64> buf; // TODO handle more than this many zombies |
|
317 TInt res = iMemAccess.GetZombies(buf); |
|
318 LeaveIfErr(res, _L("Couldn't get zombie info from driver")); |
|
319 //TInt leftOver = 0; |
|
320 //if (res > buf.MaxLength()) leftOver = (res - buf.MaxLength()) / sizeof(TUint); |
|
321 RMemoryAccess::TZombieInfo* ptr = (RMemoryAccess::TZombieInfo*)buf.Ptr(); |
|
322 TInt zombiecount = buf.Length() / sizeof(RMemoryAccess::TZombieInfo); |
|
323 for (TInt i = 0; i < zombiecount; i++) |
|
324 { |
|
325 TUint id = ptr[i].iThreadId; |
|
326 if (ContextForThread(id)) continue; // already attached to this thread, keep looking |
|
327 RThread thread; |
|
328 TInt err = iMemAccess.RThreadForceOpen(thread, id); |
|
329 if (err) |
|
330 { |
|
331 PrintWarning(_L("Couldn't open thread %u"), id); |
|
332 continue; |
|
333 } |
|
334 iTempNameBuf = thread.FullName(); |
|
335 Printf(_L("(Found thread id %u %S)\r\n"), id, &iTempNameBuf); |
|
336 if (aThreadId == 0) aThreadId = id; |
|
337 thread.Close(); |
|
338 } |
|
339 if (aThreadId == 0) |
|
340 { |
|
341 if (buf.Length()) |
|
342 Printf(_L("No zombied threads found that aren't already attached.\r\n")); |
|
343 else |
|
344 PrintWarning(_L("No zombied threads found to attach to.")); |
|
345 return; |
|
346 } |
|
347 else |
|
348 { |
|
349 Printf(_L("(Attaching to thread %u)\r\n"), aThreadId); |
|
350 } |
|
351 } |
|
352 |
|
353 SThreadContext* context = ContextForThread(aThreadId); |
|
354 if (context) |
|
355 { |
|
356 PrintWarning(_L("Already attached to thread %u, focussing it instead."), aThreadId); |
|
357 iCurrent = context; |
|
358 return; |
|
359 } |
|
360 |
|
361 context = new(ELeave) SThreadContext; |
|
362 CleanupStack::PushL(context); |
|
363 RThread& thread(context->iThread); |
|
364 CleanupClosePushL(thread); |
|
365 LeaveIfErr(iMemAccess.RThreadForceOpen(thread, aThreadId), _L("Couldn't open thread %u"), aThreadId); |
|
366 RefreshL(*context); |
|
367 iThreads.AppendL(context); |
|
368 CleanupStack::Pop(2, context); // context->iThread, context |
|
369 iCurrent = context; |
|
370 |
|
371 PrintThreadInfo(*context); |
|
372 // Don't print any more than this - it makes the output too wordy |
|
373 //PrintRegistersL(*context); |
|
374 } |
|
375 |
|
376 void CCmdFdb::PrintThreadInfo(SThreadContext& aContext) |
|
377 { |
|
378 RThread& aThread = aContext.iThread; |
|
379 TFullName name = aThread.FullName(); |
|
380 Write(_L("Name: ")); |
|
381 Write(name); |
|
382 |
|
383 Printf(_L("\r\nThread Id: %d, exit type: "), (TUint)aThread.Id()); |
|
384 TExitType exitType = aThread.ExitType(); |
|
385 if (exitType == EExitPending) |
|
386 { |
|
387 Printf(_L("Still running\r\n")); |
|
388 } |
|
389 else if (exitType == EExitKill) |
|
390 { |
|
391 Printf(_L("Kill %d\r\n"), aThread.ExitReason()); |
|
392 } |
|
393 else if (exitType == EExitTerminate) |
|
394 { |
|
395 Printf(_L("Terminate %d\r\n"), aThread.ExitReason()); |
|
396 } |
|
397 else if (exitType == EExitPanic) |
|
398 { |
|
399 TExitCategoryName exitCategory = aThread.ExitCategory(); |
|
400 Printf(_L("Panic %S %d\r\n"), &exitCategory, aThread.ExitReason()); |
|
401 } |
|
402 |
|
403 Printf(_L("User stack: base=%08x limit=%08x\r\n"), aContext.iKernelInfo.UserStackBase(), aContext.iKernelInfo.iUserStackLimit); |
|
404 Printf(_L("Kern stack: base=%08x limit=%08x\r\n"), aContext.iKernelInfo.iSupervisorStack + aContext.iKernelInfo.iSupervisorStackSize, aContext.iKernelInfo.iSupervisorStack); |
|
405 } |
|
406 |
|
407 void CCmdFdb::PrintRegistersL(SThreadContext& aThread) |
|
408 { |
|
409 PrintRegistersL(aThread, ETrue); |
|
410 PrintRegistersL(aThread, EFalse); |
|
411 } |
|
412 |
|
413 void CCmdFdb::PrintRegistersL(SThreadContext& aThread, TBool aUserMode) |
|
414 { |
|
415 if (aUserMode) |
|
416 { |
|
417 Printf(_L("User mode registers:\r\n")); |
|
418 } |
|
419 else |
|
420 { |
|
421 Printf(_L("Supervisor mode registers:\r\n")); |
|
422 } |
|
423 |
|
424 TUint32 valid = aThread.iUserValidRegisters; |
|
425 TUint32* ptr = aThread.iUserRegisters; |
|
426 if (!aUserMode) |
|
427 { |
|
428 valid = aThread.iSupervisorValidRegisters; |
|
429 ptr = aThread.iSupervisorRegisters; |
|
430 } |
|
431 |
|
432 if (valid == 0) Printf(_L("(No valid registers)\r\n")); |
|
433 // From here down is ARM specific, but since no other platforms implement this API there's no pressing need to ifdef it |
|
434 // See TArmRegSet for the ordering |
|
435 for (TInt i = 0; i < 16; i++) |
|
436 { |
|
437 if (valid & (1<<i)) |
|
438 { |
|
439 if (i < 10) Write(_L(" ")); // extra space for padding |
|
440 Printf(_L(" R%d: "), i); |
|
441 Printf(_L("%08x "), ptr[i]); |
|
442 Write(LookupSymbol(ptr[i])); |
|
443 Write(KCrLf); |
|
444 } |
|
445 } |
|
446 if (valid & (1<<16)) |
|
447 { |
|
448 Printf(_L("Flags (%cPSR): %08x\r\n"), aUserMode ? 'C' : 'S', ptr[16]); // CPSR or SPSR |
|
449 } |
|
450 if (valid & (1<<17)) |
|
451 { |
|
452 // Domain Access Control Register |
|
453 Printf(_L(" DACR: %08x\r\n"), ptr[16]); |
|
454 } |
|
455 } |
|
456 |
|
457 void CCmdFdb::PrintRegistersLRPC(SThreadContext& aThread, TBool aUserMode) |
|
458 { |
|
459 // Print these in the format of the stack trace |
|
460 TUint32 valid = aThread.iUserValidRegisters; |
|
461 TUint32* ptr = aThread.iUserRegisters; |
|
462 if (!aUserMode) |
|
463 { |
|
464 valid = aThread.iSupervisorValidRegisters; |
|
465 ptr = aThread.iSupervisorRegisters; |
|
466 } |
|
467 |
|
468 if (valid & 1<<15) |
|
469 { |
|
470 Printf(_L(" R15: %08x "), ptr[15]); |
|
471 Write(LookupSymbol(ptr[15])); |
|
472 Write(KCrLf); |
|
473 } |
|
474 if (valid & 1<<14) |
|
475 { |
|
476 Printf(_L(" R14: %08x "), ptr[14]); |
|
477 Write(LookupSymbol(ptr[14])); |
|
478 Write(KCrLf); |
|
479 } |
|
480 } |
|
481 |
|
482 void CCmdFdb::RefreshIfRunningL(SThreadContext& aContext) |
|
483 { |
|
484 if (iCurrent->iFlags.IsSet(SThreadContext::ERunning)) |
|
485 { |
|
486 // Thread is still running, we should refresh our info |
|
487 RefreshL(aContext); |
|
488 } |
|
489 } |
|
490 |
|
491 void CCmdFdb::RefreshL(SThreadContext& aContext) |
|
492 { |
|
493 aContext.iFlags.Assign(SThreadContext::ERunning, aContext.iThread.ExitType() == EExitPending); |
|
494 TUint tid = aContext.iThread.Id(); |
|
495 |
|
496 // Get registers |
|
497 TPtr8 userreg((TUint8*)&aContext.iUserRegisters[0], KMaxRegisters*sizeof(TUint32), KMaxRegisters*sizeof(TUint32)); |
|
498 userreg.FillZ(); |
|
499 aContext.iUserValidRegisters = 0; |
|
500 TInt err = iMemAccess.GetRegisters(aContext.iThread, ETrue, userreg, aContext.iUserValidRegisters); |
|
501 if (err) PrintError(err, _L("Couldn't read user registers for thread %u"), tid); |
|
502 |
|
503 TPtr8 supreg((TUint8*)&aContext.iSupervisorRegisters[0], KMaxRegisters*sizeof(TUint32), KMaxRegisters*sizeof(TUint32)); |
|
504 supreg.FillZ(); |
|
505 aContext.iSupervisorValidRegisters = 0; |
|
506 err = iMemAccess.GetRegisters(aContext.iThread, EFalse, supreg, aContext.iSupervisorValidRegisters); |
|
507 if (err) PrintError(err, _L("Couldn't read supervisor registers for thread %u\r\n"), tid); |
|
508 |
|
509 // And memaccess info |
|
510 TPckg<TThreadKernelInfo> kerninfo(aContext.iKernelInfo); |
|
511 err = iMemAccess.GetObjectInfoByHandle(EThread, RThread().Id(), aContext.iThread.Handle(), kerninfo); |
|
512 if (err) PrintError(err, _L("Couldn't read thread info from memoryaccess for thread %u\r\n"), tid); |
|
513 //aContext.iFlags.Assign(SThreadContext::ESuspended, aContext.iKernelInfo.iNThreadSuspendCount != 0); |
|
514 } |
|
515 |
|
516 TPtrC CCmdFdb::LookupSymbol(TUint32 aAddress) |
|
517 { |
|
518 TPtrC name; |
|
519 if (iSymbols) |
|
520 { |
|
521 // Try straight ROM address lookup |
|
522 TRAPD(err, name.Set(iSymbols->LookupL(aAddress))); |
|
523 if (err) PrintError(err, _L("Failed to lookup symbol")); |
|
524 if (name.Length()) return name; |
|
525 } |
|
526 // Try getting a codeseg from memaccess |
|
527 TFullName8 codesegname; |
|
528 TInt res = iMemAccess.FindAddressInCodeSegments(codesegname, (TAny*)aAddress); |
|
529 if (res >= 0) |
|
530 { |
|
531 iTempNameBuf.Copy(codesegname); |
|
532 if (iSymbols) |
|
533 { |
|
534 // Try codeseg lookup in CSymbolics (ie in a CMapFile) |
|
535 TParsePtrC parse(iTempNameBuf); |
|
536 TRAPD(err, name.Set(iSymbols->LookupL(parse.NameAndExt(), res))); |
|
537 if (err) PrintError(err, _L("Failed to lookup symbol")); |
|
538 if (name.Length()) return name; |
|
539 } |
|
540 // Otherwise fallback to just doing codeseg+offset |
|
541 iTempNameBuf.AppendFormat(_L(" + 0x%x"), res); |
|
542 return iTempNameBuf; |
|
543 } |
|
544 // Last ditch, check if euser thinks it's in ROM |
|
545 if (RFs::IsRomAddress((TAny*)aAddress)) |
|
546 { |
|
547 _LIT(KSomewhere, "[Somewhere in ROM]"); |
|
548 return KSomewhere(); |
|
549 } |
|
550 return KNullDesC(); |
|
551 } |
|
552 |
|
553 void CCmdFdb::StartCommandParsingL() |
|
554 { |
|
555 TBuf<64> historyFile; |
|
556 User::LeaveIfError(FsL().PrivatePath(historyFile)); |
|
557 historyFile.Insert(0, _L("c:")); |
|
558 historyFile.Append(_L("fdb_history")); |
|
559 Fs().CreatePrivatePath(EDriveC); |
|
560 |
|
561 iLineEditor = CLineEditor::NewL(Fs(), iConsoleAdapter, *this, *this, historyFile); |
|
562 iLineEditor->Start(KPrompt, iCommandLineBuf); |
|
563 iLineEditor->ReinstatePromptAndUserInput(); |
|
564 SetErrorReported(EFalse); |
|
565 Stdin().WaitForKey(iStatus); |
|
566 SetActive(); |
|
567 } |
|
568 |
|
569 void CCmdFdb::RunL() |
|
570 { |
|
571 if (iStatus.Int() < 0) |
|
572 { |
|
573 // iosrv dead? |
|
574 Complete(iStatus.Int()); |
|
575 return; |
|
576 } |
|
577 |
|
578 iLineEditor->HandleKey(Stdin().KeyCode(), Stdin().KeyModifiers()); |
|
579 if (iShouldExit) |
|
580 { |
|
581 Complete(KErrNone); |
|
582 } |
|
583 else if (iFinishedCommand) |
|
584 { |
|
585 iLineEditor->Start(KPrompt, iCommandLineBuf); |
|
586 iLineEditor->ReinstatePromptAndUserInput(); |
|
587 iFinishedCommand = EFalse; |
|
588 SetErrorReported(EFalse); // We need to clear this each time through |
|
589 } |
|
590 |
|
591 if (!iShouldExit) |
|
592 { |
|
593 Stdin().WaitForKey(iStatus); |
|
594 SetActive(); |
|
595 } |
|
596 } |
|
597 |
|
598 void CCmdFdb::DoCancel() |
|
599 { |
|
600 Stdin().WaitForKeyCancel(); |
|
601 } |
|
602 |
|
603 void CCmdFdb::LcCompleteLineL(TConsoleLine& aLine, const TChar& /*aEscapeChar*/) |
|
604 { |
|
605 TPtrC line = aLine.ContentsToCursor(); |
|
606 TLex lex(line); |
|
607 TPtrC cmd = lex.NextToken(); |
|
608 if (cmd != _L("b") && cmd != _L("break")) return; // We only currently do completion on the 'break' command |
|
609 |
|
610 CompleteLineWithSymbolL(aLine, lex); |
|
611 } |
|
612 |
|
613 void CCmdFdb::CompleteLineWithSymbolL(TConsoleLine& aLine, TLex& aLex) |
|
614 { |
|
615 // Have we got as far as a codeseg? We don't support completing on codeseg yet |
|
616 TPtrC codeseg = aLex.NextToken(); |
|
617 TPtrC ext = codeseg.Right(4); |
|
618 if (ext != _L(".dll") && ext != _L(".exe")) return; |
|
619 |
|
620 aLex.SkipSpace(); |
|
621 TInt symbolStartPos = aLex.Offset(); |
|
622 TPtrC symbol = aLex.Remainder(); |
|
623 RLtkBuf buf; |
|
624 buf.CreateLC(512); |
|
625 buf.Copy(symbol); |
|
626 CDesC16Array* suggestions = new(ELeave) CDesC16ArrayFlat(32); |
|
627 CleanupStack::PushL(suggestions); |
|
628 //TODO this can be slow first time, need some UI to warn the user |
|
629 iSymbols->CompleteL(codeseg, buf, *suggestions); |
|
630 if (buf.Length() > symbol.Length()) |
|
631 { |
|
632 aLine.Replace(symbolStartPos, buf); |
|
633 } |
|
634 // If the tab added any chars we don't show choices |
|
635 else if (suggestions->Count() > 1) |
|
636 { |
|
637 buf.Zero(); |
|
638 for (TInt i = 0; i < suggestions->Count(); i++) |
|
639 { |
|
640 if (i > 0) buf.AppendL('\t'); |
|
641 buf.AppendL((*suggestions)[i]); |
|
642 } |
|
643 aLine.PrintCompletionPossibilitiesL(buf); |
|
644 } |
|
645 |
|
646 CleanupStack::PopAndDestroy(suggestions); |
|
647 CleanupStack::PopAndDestroy(&buf); |
|
648 } |
|
649 |
|
650 void CCmdFdb::LeoHandleLine(const TDesC& aLine) |
|
651 { |
|
652 iFinishedCommand = ETrue; |
|
653 if (aLine.Length()) |
|
654 { |
|
655 TRAPD(err, ProcessLineL(aLine)); |
|
656 if (err) |
|
657 { |
|
658 PrintError(err, _L("Error executing command")); |
|
659 } |
|
660 } |
|
661 } |
|
662 |
|
663 void CCmdFdb::ProcessLineL(const TDesC& aLine) |
|
664 { |
|
665 TLex lex(aLine); |
|
666 TPtrC cmd = lex.NextToken(); |
|
667 lex.SkipSpace(); |
|
668 |
|
669 char ch = 0; |
|
670 if (cmd.Length() == 1) |
|
671 { |
|
672 ch = cmd[0]; |
|
673 } |
|
674 |
|
675 _LIT(KHelp, "help"); |
|
676 _LIT(KExit, "exit"); |
|
677 _LIT(KAttach, "attach"); |
|
678 _LIT(KDetach, "detach"); |
|
679 _LIT(KFocus, "focus"); |
|
680 _LIT(KRegisters, "registers"); |
|
681 _LIT(KLookup, "lookup"); |
|
682 _LIT(KStack, "stack"); |
|
683 _LIT(KKstack, "kstack"); |
|
684 _LIT(KList, "list"); |
|
685 _LIT(KMem, "mem"); |
|
686 _LIT(KUninstall, "uninstall"); |
|
687 _LIT(KBrowse, "browse"); |
|
688 _LIT(KBreak, "break"); |
|
689 _LIT(KContinue, "continue"); |
|
690 _LIT(KClear, "clear"); |
|
691 _LIT(KLoad, "load"); |
|
692 if (cmd == KHelp || ch == 'h') |
|
693 { |
|
694 ShowHelpTextL(); |
|
695 } |
|
696 else if (cmd == KExit || ch == 'x') |
|
697 { |
|
698 iShouldExit = ETrue; // TODO check whether a detach is desired |
|
699 } |
|
700 else if (cmd == KUninstall || ch == 'u') |
|
701 { |
|
702 Printf(_L("Uninstalling thread hook, clearing all breakpoints, freeing all zombie threads. Undertakers will now get notified of the threads' exits.\r\n")); |
|
703 iMemAccess.SetZombieDebugMode(0); |
|
704 iShouldExit = ETrue; |
|
705 } |
|
706 else if (cmd == KAttach || ch == 'a') |
|
707 { |
|
708 TUint threadId = 0; |
|
709 lex.Val(threadId); |
|
710 AttachL(threadId); |
|
711 } |
|
712 else if (cmd == KFocus || ch == 'f') |
|
713 { |
|
714 TUint threadId = 0; |
|
715 lex.Val(threadId); |
|
716 if (threadId == 0) |
|
717 { |
|
718 PrintThreadInfo(CurrentL()); |
|
719 } |
|
720 else |
|
721 { |
|
722 FocusL(threadId); |
|
723 } |
|
724 } |
|
725 else if (cmd == KRegisters || ch == 'r') |
|
726 { |
|
727 PrintRegistersL(CurrentL()); |
|
728 } |
|
729 else if (cmd == KLookup || ch == '?') |
|
730 { |
|
731 TUint32 addr = LtkUtils::HexLexL(lex); |
|
732 Printf(_L("%08x: "), addr); |
|
733 Write(LookupSymbol(addr)); |
|
734 Write(KCrLf); |
|
735 } |
|
736 else if (cmd == KDetach || ch == 'd') |
|
737 { |
|
738 TUint threadId = 0; |
|
739 lex.Val(threadId); |
|
740 SThreadContext* thread = NULL; |
|
741 if (threadId == 0) |
|
742 { |
|
743 // No point refreshing, all it will do is possibly complain about reading the registers (which looks confusing) |
|
744 thread = &CurrentNoRefreshL(); |
|
745 } |
|
746 else |
|
747 { |
|
748 thread = ContextForThread(threadId); |
|
749 if (!thread) LeaveIfErr(KErrNotFound, _L("Thread %u is not currently attached."), threadId); |
|
750 } |
|
751 Detach(thread); |
|
752 } |
|
753 else if (cmd == KStack || ch == 't') |
|
754 { |
|
755 TBool all = lex.NextToken() == _L("all"); |
|
756 SThreadContext& c = CurrentL(); |
|
757 TUint32 start = c.iKernelInfo.iUserStackLimit; |
|
758 TUint32 end = c.iKernelInfo.UserStackBase(); |
|
759 if (start == 0) |
|
760 { |
|
761 Printf(_L("No user stack for this thread.\r\n")); |
|
762 } |
|
763 else |
|
764 { |
|
765 PrintRegistersLRPC(c, ETrue); |
|
766 if ((c.iUserValidRegisters & (1 << 13)) && Rng(start, c.iUserRegisters[13], end)) start = c.iUserRegisters[13]; |
|
767 PrintMemL(c.iThread.Id(), start, end, all ? EAllData : EJustSymbols); |
|
768 } |
|
769 } |
|
770 else if (cmd == KKstack || ch == 'k') |
|
771 { |
|
772 TBool all = lex.NextToken() == _L("all"); |
|
773 SThreadContext& c = CurrentL(); |
|
774 TUint32 start = c.iKernelInfo.iSupervisorStack; |
|
775 TUint32 end = start + c.iKernelInfo.iSupervisorStackSize; |
|
776 if (start == 0) |
|
777 { |
|
778 Printf(_L("Couldn't find kernel stack for this thread (!?).\r\n")); |
|
779 } |
|
780 else |
|
781 { |
|
782 PrintRegistersLRPC(c, EFalse); |
|
783 if ((c.iSupervisorValidRegisters & (1 << 13)) && Rng(start, c.iSupervisorRegisters[13], end)) start = c.iSupervisorRegisters[13]; |
|
784 PrintMemL(0, start, end, all ? EAllData : EJustSymbols); // zero is the null thread, which is a kernel thread thus has the address space we're interested in |
|
785 } |
|
786 } |
|
787 else if (cmd == KList || ch == 'l') |
|
788 { |
|
789 BrrrrrainsL(); |
|
790 } |
|
791 else if (cmd == KMem || ch == 'm') |
|
792 { |
|
793 TUint32 start = LtkUtils::HexLexL(lex); |
|
794 lex.SkipSpace(); |
|
795 TUint32 len = LtkUtils::HexLexL(lex); |
|
796 PrintMemL(CurrentL().iThread.Id(), start, start+len, EHexDump); |
|
797 } |
|
798 else if (cmd == KBrowse) |
|
799 { |
|
800 TPtrC remainder = lex.Remainder(); |
|
801 TLinAddr addr = 0; |
|
802 if (remainder.Length() == 0 || remainder == _L("heap")) |
|
803 { |
|
804 //TODO |
|
805 } |
|
806 else if (remainder == _L("stack")) |
|
807 { |
|
808 //TODO |
|
809 } |
|
810 else |
|
811 { |
|
812 addr = LtkUtils::HexLexL(lex); |
|
813 } |
|
814 StartInteractiveViewL(addr); |
|
815 } |
|
816 else if (cmd == KBreak || ch == 'b') |
|
817 { |
|
818 TInt res = KErrNone; |
|
819 TBool set = ETrue; |
|
820 TPtrC remainder = lex.Remainder(); |
|
821 if (remainder.Length() == 0) |
|
822 { |
|
823 ShowBreakpointsL(); |
|
824 set = EFalse; |
|
825 } |
|
826 else if (remainder == _L("test")) |
|
827 { |
|
828 // This is undocumented, for testing only |
|
829 RThread me; |
|
830 res = iMemAccess.SetBreakpoint(me, (TLinAddr)&LtkUtils::RawPrint); |
|
831 LeaveIfErr(res, _L("Couldn't set test breakpoint")); |
|
832 RThread testThread; |
|
833 LeaveIfErr(testThread.Create(_L("BreakpointTestThread"), &BreakTest, 8192, NULL, NULL), _L("Couldn't create test thread")); |
|
834 testThread.Resume(); |
|
835 testThread.Close(); |
|
836 } |
|
837 else if (remainder == _L("testltk")) |
|
838 { |
|
839 // This is undocumented, for testing only |
|
840 RThread testThread; |
|
841 LeaveIfErr(testThread.Create(_L("BreakpointLtkTestThread"), &BreakTestLtk, 8192, NULL, NULL), _L("Couldn't create test thread")); |
|
842 testThread.Resume(); |
|
843 testThread.Close(); |
|
844 set = EFalse; |
|
845 } |
|
846 else if (remainder == _L("testhw")) |
|
847 { |
|
848 // This is undocumented, for testing only |
|
849 RThread testThread; |
|
850 LeaveIfErr(testThread.Create(_L("BreakpointTestThread"), &BreakTest, 8192, NULL, NULL), _L("Couldn't create test thread")); |
|
851 res = iMemAccess.SetBreakpoint(testThread, (TLinAddr)&LtkUtils::RawPrint); |
|
852 LeaveIfErr(res, _L("Couldn't set test breakpoint")); |
|
853 testThread.Resume(); |
|
854 testThread.Close(); |
|
855 } |
|
856 else if (remainder == _L("testcond")) |
|
857 { |
|
858 // This is undocumented, for testing only |
|
859 RThread testThread; |
|
860 LeaveIfErr(testThread.Create(_L("BreakpointTestThread"), &BreakTestCond, 8192, NULL, NULL), _L("Couldn't create test thread")); |
|
861 RMemoryAccess::TPredicate condition; |
|
862 LeaveIfErr(condition.AddCondition(RMemoryAccess::TPredicate::ESignedEq, 0, (TUint)-5), _L("Couldn't add condition")); |
|
863 res = iMemAccess.SetBreakpoint(testThread, (TLinAddr)&User::Leave, &condition); |
|
864 LeaveIfErr(res, _L("Couldn't set test breakpoint")); |
|
865 testThread.Resume(); |
|
866 testThread.Close(); |
|
867 } |
|
868 else |
|
869 { |
|
870 TUint addr; |
|
871 TInt err = HexLex(lex, addr); |
|
872 if (err) |
|
873 { |
|
874 // Try codeseg and symbol name |
|
875 TPtrC codeseg = lex.NextToken(); |
|
876 lex.SkipSpace(); |
|
877 TPtrC name = lex.Remainder(); |
|
878 // Hmm symbols can have spaces in, how to distinguish the condition? Assume if the last word starts with an 'r' it's a condition. Not very nice. |
|
879 TInt space = name.LocateReverse(' '); |
|
880 RMemoryAccess::TPredicate condition; |
|
881 if (space >= 0 && space+1 < name.Length() && name[space+1] == 'r') |
|
882 { |
|
883 name.Set(name.Left(space)); |
|
884 lex.Inc(name.Length()); |
|
885 while (!lex.Eos()) |
|
886 { |
|
887 CheckForConditionL(lex, condition); |
|
888 } |
|
889 } |
|
890 |
|
891 TUint offset = 0; |
|
892 TRAPL(offset = iSymbols->CodesegOffsetFromSymbolNameL(codeseg, name), _L("Couldn't find offset of symbol '%S' in codeseg '%S'"), &name, &codeseg); |
|
893 RLtkBuf8 codeseg8; |
|
894 CleanupClosePushL(codeseg8); |
|
895 codeseg8.AppendL(codeseg); |
|
896 res = iMemAccess.SetSymbolicBreakpoint(CurrentL().iThread, codeseg8, offset, &condition); |
|
897 LeaveIfErr(res, _L("Couldn't create symbolic breakpoint")); |
|
898 CleanupStack::PopAndDestroy(&codeseg8); |
|
899 } |
|
900 else |
|
901 { |
|
902 RMemoryAccess::TPredicate condition; |
|
903 while (!lex.Eos()) |
|
904 { |
|
905 CheckForConditionL(lex, condition); |
|
906 } |
|
907 res = iMemAccess.SetBreakpoint(CurrentL().iThread, addr, &condition); |
|
908 LeaveIfErr(res, _L("Couldn't create breakpoint")); |
|
909 Printf(_L("Breakpoint created at 0x%08x "), addr); |
|
910 Write(LookupSymbol(addr)); |
|
911 Write(KCrLf); |
|
912 } |
|
913 } |
|
914 |
|
915 if (set) |
|
916 { |
|
917 if (res & RMemoryAccess::TBreakpointInfo::EHardware) |
|
918 { |
|
919 Printf(_L("Hardware breakpoint %d set.\r\n"), res & ~RMemoryAccess::TBreakpointInfo::EHardware); |
|
920 } |
|
921 else if (res == 0) |
|
922 { |
|
923 Printf(_L("Pending breakpoint set (Note these don't work yet!)\r\n")); |
|
924 } |
|
925 else |
|
926 { |
|
927 Printf(_L("Breakpoint %d set.\r\n"), res); |
|
928 } |
|
929 } |
|
930 } |
|
931 else if (cmd == KContinue || ch =='c') |
|
932 { |
|
933 TInt err = iMemAccess.ContinueFromBreakpoint(CurrentL().iThread); |
|
934 LeaveIfErr(err, _L("Couldn't continue - is the thread definitely stopped on a breakpoint?")); |
|
935 } |
|
936 else if (cmd == KClear) |
|
937 { |
|
938 TPtrC remainder = lex.Remainder(); |
|
939 if (remainder.Length() == 0) |
|
940 { |
|
941 ClearAllBreakpointsL(); |
|
942 } |
|
943 else |
|
944 { |
|
945 TInt n; |
|
946 User::LeaveIfError(lex.Val(n)); |
|
947 LeaveIfErr(iMemAccess.ClearBreakpoint(n), _L("Couldn't clear breakpoint %d"), n); |
|
948 } |
|
949 } |
|
950 else if (cmd == KLoad) |
|
951 { |
|
952 TPtrC path = lex.Remainder(); |
|
953 if (path.Right(5).CompareF(_L(".bsym")) == 0) |
|
954 { |
|
955 //Printf(_L("Loading symbolics... ")); // It can take a while... |
|
956 iSymbols->AddBsymFileL(path); |
|
957 //Printf(_L("done.\r\n")); |
|
958 } |
|
959 else |
|
960 { |
|
961 iSymbols->SetFallbackMapFileDirL(path); |
|
962 } |
|
963 } |
|
964 else |
|
965 { |
|
966 PrintError(KErrNotFound, _L("Unrecognised command '%S'. Try 'help'."), &cmd); |
|
967 } |
|
968 } |
|
969 |
|
970 CCmdFdb::SThreadContext* CCmdFdb::ContextForThread(TUint aThreadId) const |
|
971 { |
|
972 for (TInt i = 0; i < iThreads.Count(); i++) |
|
973 { |
|
974 if ((TUint)iThreads[i]->iThread.Id() == aThreadId) |
|
975 { |
|
976 return iThreads[i]; |
|
977 } |
|
978 } |
|
979 return NULL; |
|
980 } |
|
981 |
|
982 CCmdFdb::SThreadContext& CCmdFdb::CurrentL() |
|
983 { |
|
984 CCmdFdb::SThreadContext& current = CurrentNoRefreshL(); |
|
985 RefreshIfRunningL(current); |
|
986 return current; |
|
987 } |
|
988 |
|
989 CCmdFdb::SThreadContext& CCmdFdb::CurrentNoRefreshL() |
|
990 { |
|
991 if (!iCurrent) |
|
992 { |
|
993 LeaveIfErr(KErrNotReady, _L("No currently focussed thread")); |
|
994 } |
|
995 return *iCurrent; |
|
996 } |
|
997 |
|
998 void CCmdFdb::FocusL(TUint aThreadId) |
|
999 { |
|
1000 SThreadContext* c = ContextForThread(aThreadId); |
|
1001 if (c) iCurrent = c; |
|
1002 else |
|
1003 { |
|
1004 LeaveIfErr(KErrNotFound, _L("Couldn't find thread id %u in the attached threads. Do you need to do 'attach %u'?"), aThreadId, aThreadId); |
|
1005 } |
|
1006 } |
|
1007 |
|
1008 void CCmdFdb::Detach(SThreadContext* aContext) |
|
1009 { |
|
1010 TInt err = iMemAccess.ReleaseZombie(aContext->iThread); |
|
1011 if (err && aContext->iFlags.IsSet(SThreadContext::ERunning)) |
|
1012 { |
|
1013 // Don't complain about driver if the thread wasn't actually zombied |
|
1014 PrintError(err, _L("Driver couldn't find zombie thread")); |
|
1015 } |
|
1016 |
|
1017 TInt arrayPos = iThreads.Find(aContext); |
|
1018 iThreads.Remove(arrayPos); |
|
1019 if (iCurrent == aContext) iCurrent = NULL; |
|
1020 aContext->iThread.Close(); |
|
1021 delete aContext; |
|
1022 } |
|
1023 |
|
1024 void CCmdFdb::PrintMemL(TUint aThreadId, TUint32 aStart, TUint32 aEnd, TPrintMode aMode) |
|
1025 { |
|
1026 // word-align start and end |
|
1027 aStart &= ~3; |
|
1028 aEnd = (aEnd+3) & (~3); |
|
1029 |
|
1030 TInt size = aEnd - aStart; |
|
1031 RBuf8 mem; |
|
1032 CleanupClosePushL(mem); |
|
1033 mem.CreateL(size); |
|
1034 |
|
1035 TThreadMemoryAccessParamsBuf params; |
|
1036 params().iId = aThreadId; |
|
1037 params().iAddr = (TUint8*)aStart; |
|
1038 params().iSize = size; |
|
1039 |
|
1040 TInt err = iMemAccess.GetThreadMem(params, mem); |
|
1041 LeaveIfErr(err, _L("Couldn't read thread memory %08x-%08x"), aStart, aEnd); |
|
1042 |
|
1043 if (aMode == EHexDump) |
|
1044 { |
|
1045 TInt offset = (TInt)aStart; |
|
1046 LtkUtils::HexDumpToOutput(mem, Stdout(), offset); |
|
1047 } |
|
1048 else |
|
1049 { |
|
1050 const TUint32* ptr = (const TUint32*)mem.Ptr(); |
|
1051 const TInt count = mem.Size() / 4; |
|
1052 for (TInt i = 0; i < count; i++) |
|
1053 { |
|
1054 TUint32 word = ptr[i]; |
|
1055 TBool print = aMode == EAllData || IsSymbol(word); |
|
1056 if (print) |
|
1057 { |
|
1058 Printf(_L("%08x: %08x "), aStart + i*4, word); |
|
1059 Write(LookupSymbol(word)); |
|
1060 Write(KCrLf); |
|
1061 } |
|
1062 } |
|
1063 } |
|
1064 CleanupStack::PopAndDestroy(&mem); |
|
1065 } |
|
1066 |
|
1067 void CCmdFdb::BrrrrrainsL() |
|
1068 { |
|
1069 RBuf8 buf; |
|
1070 CleanupClosePushL(buf); |
|
1071 buf.CreateL(1024); |
|
1072 TInt res = iMemAccess.GetZombies(buf); |
|
1073 LeaveIfErr(res, _L("Couldn't get zombie info from driver")); |
|
1074 RMemoryAccess::TZombieInfo* ptr = (RMemoryAccess::TZombieInfo*)buf.Ptr(); |
|
1075 const TInt zombiecount = buf.Length()/sizeof(RMemoryAccess::TZombieInfo); |
|
1076 |
|
1077 // Go through all the zombies |
|
1078 for (TInt i = 0; i < zombiecount; i++) |
|
1079 { |
|
1080 TUint id = ptr[i].iThreadId; |
|
1081 SThreadContext* context = ContextForThread(id); |
|
1082 char stat = '-'; |
|
1083 char suspended = '-'; |
|
1084 if (context) |
|
1085 { |
|
1086 stat = 'a'; |
|
1087 if (context == iCurrent) stat = '*'; |
|
1088 iTempNameBuf = context->iThread.FullName(); |
|
1089 } |
|
1090 else |
|
1091 { |
|
1092 RThread thread; |
|
1093 TInt err = iMemAccess.RThreadForceOpen(thread, id); |
|
1094 if (err) |
|
1095 { |
|
1096 PrintWarning(_L("Couldn't open thread %u"), id); |
|
1097 continue; |
|
1098 } |
|
1099 iTempNameBuf = thread.FullName(); |
|
1100 thread.Close(); |
|
1101 } |
|
1102 if (ptr[i].iFlags & RMemoryAccess::TZombieInfo::ESuspended) suspended = 's'; |
|
1103 if (ptr[i].iFlags & RMemoryAccess::TZombieInfo::EBreakpoint) suspended = 'b'; |
|
1104 |
|
1105 Printf(_L("%c%c %u "), stat, suspended, id); |
|
1106 Write(iTempNameBuf); |
|
1107 Write(KCrLf); |
|
1108 } |
|
1109 |
|
1110 // Now do any attached threads that aren't zombied |
|
1111 for (TInt i = 0; i < iThreads.Count(); i++) |
|
1112 { |
|
1113 SThreadContext* c = iThreads[i]; |
|
1114 TUint id = (TUint)c->iThread.Id(); |
|
1115 RMemoryAccess::TZombieInfo dummy; dummy.iThreadId = id; |
|
1116 TBool foundInZombies = EFalse; |
|
1117 if (zombiecount) foundInZombies = RArray<RMemoryAccess::TZombieInfo>(sizeof(RMemoryAccess::TZombieInfo), ptr, zombiecount).Find(dummy) != KErrNotFound; |
|
1118 if (!foundInZombies) |
|
1119 { |
|
1120 char stat = 'a'; |
|
1121 if (c == iCurrent) stat = '*'; |
|
1122 iTempNameBuf = c->iThread.FullName(); |
|
1123 Printf(_L("%c- %u "), stat, id); |
|
1124 Write(iTempNameBuf); |
|
1125 Write(KCrLf); |
|
1126 } |
|
1127 } |
|
1128 |
|
1129 CleanupStack::PopAndDestroy(&buf); |
|
1130 } |
|
1131 |
|
1132 void CCmdFdb::ShowHelpTextL() |
|
1133 { |
|
1134 // Possible TODOs: hEap, Save, Ymodem? |
|
1135 _LIT(KStartOfCommands, "SUPPORTED COMMANDS\r\n\r\n"); |
|
1136 CTextBuffer* helpText = const_cast<CTextBuffer*>(GetHelpTextL()); |
|
1137 TInt found = helpText->Descriptor().Find(KStartOfCommands); |
|
1138 helpText->Delete(0, found + KStartOfCommands().Length()); |
|
1139 helpText->Write(Stdout()); |
|
1140 delete helpText; |
|
1141 } |
|
1142 |
|
1143 TBool CCmdFdb::IsSymbol(TUint aAddress) const |
|
1144 { |
|
1145 // Ranges probably not perfect, seem to be roughly ok though |
|
1146 TBool okRange = Rng(0x70000000u, aAddress, 0xA0000000u) || Rng(0xC0000000u, aAddress, 0xFC000000u); |
|
1147 return okRange && aAddress != 0xDEDEDEDE && aAddress != 0xAAAAAAAA && aAddress != 0xBBBBBBBB && aAddress != 0xCCCCCCCC; |
|
1148 } |
|
1149 |
|
1150 void CCmdFdb::StartInteractiveViewL(TLinAddr aAddress) |
|
1151 { |
|
1152 iMemStart = aAddress & (~3); |
|
1153 if (iMemStart >= iCurrent->iKernelInfo.iUserStackLimit && iMemStart <= iCurrent->iKernelInfo.UserStackBase()) |
|
1154 { |
|
1155 iMemoryViewType = EStack; |
|
1156 } |
|
1157 else //TODO EHeap |
|
1158 { |
|
1159 iMemoryViewType = EUnspecified; |
|
1160 } |
|
1161 |
|
1162 iMemBuf.Zero(); |
|
1163 TSize consoleSize(80,24); |
|
1164 Stdout().GetScreenSize(consoleSize); |
|
1165 iMemBuf.ReAllocL(consoleSize.iHeight * 16); // At most we display 16 bytes per line |
|
1166 TInt numBytesOnLine = 16; // By default |
|
1167 if (iMemoryViewType == EStack) numBytesOnLine = 4; |
|
1168 TInt numBytesOnScreen = numBytesOnLine * consoleSize.iHeight; |
|
1169 |
|
1170 TLinAddr lastAddr = 0; |
|
1171 for (;;) |
|
1172 { |
|
1173 iMemStart &= ~3; // Just checking |
|
1174 TBool update = (iMemStart != lastAddr); |
|
1175 lastAddr = iMemStart; |
|
1176 DrawMemViewL(update); |
|
1177 TUint key = Stdin().ReadKey(); |
|
1178 switch (key) |
|
1179 { |
|
1180 case EKeyPageUp: |
|
1181 iMemStart -= numBytesOnScreen; |
|
1182 break; |
|
1183 case EKeyPageDown: |
|
1184 iMemStart += numBytesOnScreen; |
|
1185 break; |
|
1186 case EKeyUpArrow: |
|
1187 iMemStart -= numBytesOnLine; |
|
1188 break; |
|
1189 case EKeyDownArrow: |
|
1190 iMemStart += numBytesOnLine; |
|
1191 break; |
|
1192 case 'q': |
|
1193 case 'x': |
|
1194 case EKeyEscape: |
|
1195 //Stdout().SetAttributesL(ConsoleAttributes::ENone); |
|
1196 return; |
|
1197 default: |
|
1198 break; |
|
1199 } |
|
1200 } |
|
1201 } |
|
1202 |
|
1203 void CCmdFdb::UpdateMemForMemViewL() |
|
1204 { |
|
1205 iMemBuf.Zero(); |
|
1206 |
|
1207 TThreadMemoryAccessParamsBuf params; |
|
1208 params().iId = iCurrent->iThread.Id(); |
|
1209 params().iAddr = (TUint8*)iMemStart; |
|
1210 params().iSize = iMemBuf.MaxSize(); |
|
1211 |
|
1212 TInt err = iMemAccess.GetThreadMem(params, iMemBuf); |
|
1213 if (err) PrintError(err, _L("Couldn't read thread memory %08x-%08x"), params().iAddr, params().iAddr+params().iSize); |
|
1214 } |
|
1215 |
|
1216 /* |
|
1217 class TFocusable |
|
1218 { |
|
1219 public: |
|
1220 TLinAddr iAddr; |
|
1221 enum TType |
|
1222 { |
|
1223 EHeapPtr, |
|
1224 } |
|
1225 } |
|
1226 */ |
|
1227 |
|
1228 void CCmdFdb::DrawMemViewL(TBool aUpdate) |
|
1229 { |
|
1230 Stdout().ClearScreen(); |
|
1231 if (aUpdate) UpdateMemForMemViewL(); |
|
1232 |
|
1233 |
|
1234 // This is a really messy function. Can't seem to find a way of making it easier to read. |
|
1235 const ConsoleAttributes::TAttributes KNormal(ConsoleAttributes::ENone, ConsoleAttributes::EBlack, ConsoleAttributes::EWhite); |
|
1236 const ConsoleAttributes::TAttributes KSymbol(0, ConsoleAttributes::ERed, ConsoleAttributes::EUnchanged); |
|
1237 const ConsoleAttributes::TAttributes KDescriptor(0, ConsoleAttributes::EUnchanged, ConsoleAttributes::EYellow); |
|
1238 //const ConsoleAttributes::TAttributes KHeap(0, ConsoleAttributes::EUnchanged, ConsoleAttributes::ECyan); |
|
1239 //const ConsoleAttributes::TAttributes KHeapAlternate(0, ConsoleAttributes::EUnchanged, ConsoleAttributes::EBlue); |
|
1240 enum { ENoColor = 0, ESymbol = 1, EDescriptor = 2 }; |
|
1241 |
|
1242 TSize consoleSize(80,24); |
|
1243 Stdout().GetScreenSize(consoleSize); |
|
1244 TUint8 const*const bptr = iMemBuf.Ptr(); |
|
1245 TUint32 const*const ptr = (TUint32 const*)bptr; |
|
1246 //iLinks.Reset(); |
|
1247 CTextBuffer* text = CTextBuffer::NewLC(1024); |
|
1248 text->SetAttributesL(KNormal); |
|
1249 |
|
1250 TInt numBytesOnLine = 16; // By default |
|
1251 if (iMemoryViewType == EStack) |
|
1252 { |
|
1253 numBytesOnLine = 4; |
|
1254 } |
|
1255 //TInt numBytesOnScreen = numBytesOnLine * consoleSize.iHeight; |
|
1256 TInt remainingInDescriptor = 0; // Not in descriptor initially |
|
1257 //TInt remainingInHeapCell = 0; // Not worrying about heap cells right now |
|
1258 |
|
1259 for (TInt line = 0; line < consoleSize.iHeight - 1 && line*numBytesOnLine < iMemBuf.Length(); line++) |
|
1260 { |
|
1261 TBuf8<16> colorBuf; colorBuf.SetLength(colorBuf.MaxLength()); |
|
1262 TUint8* colorBufPtr = (TUint8*)colorBuf.Ptr(); |
|
1263 Mem::Fill(colorBufPtr, 16, ENoColor); |
|
1264 text->AppendFormatL(_L("%08x: "), iMemStart + line*numBytesOnLine); |
|
1265 |
|
1266 const TInt idxForLine = line * numBytesOnLine; |
|
1267 TInt i; |
|
1268 for (i = 0; i < numBytesOnLine; i += 4, colorBufPtr += 4) |
|
1269 { |
|
1270 const TInt idxInBuf = idxForLine + i; |
|
1271 if (idxInBuf >= iMemBuf.Length()) break; |
|
1272 |
|
1273 if (remainingInDescriptor == 0) remainingInDescriptor = IsDescriptorHeader(bptr + idxInBuf, 256); |
|
1274 TInt runLen = 0; |
|
1275 if (remainingInDescriptor > 0) |
|
1276 { |
|
1277 text->SetAttributesL(KDescriptor); |
|
1278 runLen = Min(remainingInDescriptor, numBytesOnLine-i); |
|
1279 if (runLen > 4) runLen = 4; // We only do up to 4 bytes at a time |
|
1280 Mem::Fill(colorBufPtr, runLen, EDescriptor); |
|
1281 } |
|
1282 |
|
1283 // Check for symbols |
|
1284 TUint32 word = ptr[idxInBuf / 4]; |
|
1285 if (IsSymbol(word)) |
|
1286 { |
|
1287 text->SetAttributesL(KSymbol); |
|
1288 colorBufPtr[0] |= ESymbol; |
|
1289 colorBufPtr[1] |= ESymbol; |
|
1290 colorBufPtr[2] |= ESymbol; |
|
1291 colorBufPtr[3] |= ESymbol; |
|
1292 if (iMemoryViewType == EStack) |
|
1293 { |
|
1294 TPtrC symb = LookupSymbol(word); |
|
1295 text->AppendFormatL(_L("%08x %S"), word, &symb); |
|
1296 text->SetAttributesL(KNormal); |
|
1297 text->AppendL(KCrLf); |
|
1298 continue; |
|
1299 } |
|
1300 } |
|
1301 |
|
1302 // Actually print the hex bytes |
|
1303 for (TInt ch = 0; ch < 4; ch++) |
|
1304 { |
|
1305 text->AppendFormatL(_L("%02X"), bptr[idxInBuf+ch]); |
|
1306 if (runLen) |
|
1307 { |
|
1308 remainingInDescriptor--; |
|
1309 runLen--; |
|
1310 if (remainingInDescriptor == 0) text->SetAttributesL(KNormal); // If we've just finished a run we clear the formatting before the space |
|
1311 } |
|
1312 text->AppendL(' '); |
|
1313 } |
|
1314 if (((i+4) % 16) == 0) text->SetAttributesL(KNormal); // In preparation for printing the ascii |
|
1315 if (((i+4) % 8) == 0) text->AppendL(' '); // Extra space every 8th char |
|
1316 } |
|
1317 |
|
1318 TInt rem = numBytesOnLine - i; |
|
1319 if (rem > 0) |
|
1320 { |
|
1321 // Need to fill in spaces for the hex bytes we don't have |
|
1322 _LIT(K3Space, " "); |
|
1323 while (rem--) text->AppendL(K3Space); |
|
1324 // And the final 2 spaces before the ascii |
|
1325 text->AppendL(' '); |
|
1326 text->AppendL(' '); |
|
1327 } |
|
1328 |
|
1329 |
|
1330 // Time to print the ascii |
|
1331 const TInt max = Min(numBytesOnLine, iMemBuf.Length() - idxForLine); |
|
1332 for (TInt j = 0; j < max; j++) |
|
1333 { |
|
1334 char ch = bptr[idxForLine + j]; |
|
1335 if (ch < 32 || ch >= 128) ch = '.'; |
|
1336 text->SetAttributesL(KNormal); |
|
1337 if (colorBuf[j] & EDescriptor) text->SetAttributesL(KDescriptor); |
|
1338 if (colorBuf[j] & ESymbol) text->SetAttributesL(KSymbol); |
|
1339 text->AppendL(ch); |
|
1340 } |
|
1341 text->SetAttributesL(KNormal); |
|
1342 text->AppendL(KCrLf); |
|
1343 } |
|
1344 |
|
1345 text->SetAttributesL(ConsoleAttributes::ENone); |
|
1346 text->Write(Stdout()); |
|
1347 CleanupStack::PopAndDestroy(text); |
|
1348 } |
|
1349 |
|
1350 /*static*/ TInt CCmdFdb::IsDescriptorHeader(TUint8 const* aHeaderPtr, TInt aMaxLen) |
|
1351 { |
|
1352 if (((TLinAddr)aHeaderPtr & 0x3) != 0) return 0; // Not aligned |
|
1353 TInt type = *(TUint32*)aHeaderPtr >> 28; |
|
1354 |
|
1355 if ((type == EPtr || type == EBufCPtr) && aMaxLen >= 12) return 12; // Len + maxlen + ptr |
|
1356 else if (type == EPtrC && aMaxLen >= 8) return 8; // Len + ptr |
|
1357 else if (type == EBuf || type == EBufC) |
|
1358 { |
|
1359 TInt len = (*(TInt32 const*)aHeaderPtr) & 0xfffffff; |
|
1360 if (len > aMaxLen || (type == 0 && len == 0)) return 0; |
|
1361 // Take a stab at whether it's a 16-bit descriptor |
|
1362 TInt wideness = 1; |
|
1363 TInt bufOffset = (type == EBuf ? 8 : 4); |
|
1364 TUint16 const* wptr = (TUint16 const*)(aHeaderPtr + bufOffset); |
|
1365 if (len > 4 && wptr[0] < 256 && wptr[1] < 256) wideness = 2; |
|
1366 return bufOffset + len * wideness; // Add 4 so the header itself is included in the calculation |
|
1367 } |
|
1368 else |
|
1369 { |
|
1370 return 0; |
|
1371 } |
|
1372 } |
|
1373 |
|
1374 void CCmdFdb::ShowBreakpointsL() |
|
1375 { |
|
1376 RBuf8 buf; |
|
1377 CleanupClosePushL(buf); |
|
1378 buf.CreateL(1024); |
|
1379 LeaveIfErr(iMemAccess.GetBreakpoints(buf), _L("Couldn't read breakpoint information")); |
|
1380 RMemoryAccess::TBreakpointInfo* bs = (RMemoryAccess::TBreakpointInfo*)buf.Ptr(); |
|
1381 RLtkBuf desc; |
|
1382 desc.CreateL(256); |
|
1383 TInt count = buf.Length() / sizeof(RMemoryAccess::TBreakpointInfo); |
|
1384 if (count) |
|
1385 { |
|
1386 for (TInt i = 0; i < count; i++) |
|
1387 { |
|
1388 RMemoryAccess::TBreakpointInfo& b = bs[i]; |
|
1389 Printf(_L("Breakpoint %d (thread id %u): "), b.iBreakpointId, b.iThreadId); |
|
1390 Write(LookupSymbol(b.iAddress)); |
|
1391 TBool brackets = b.iFlags & (RMemoryAccess::TBreakpointInfo::EPending | b.iFlags & RMemoryAccess::TBreakpointInfo::EHardware) || !(b.iFlags & RMemoryAccess::TBreakpointInfo::EEnabled) || b.iCondition.HasConditions(); |
|
1392 if (brackets) Write(_L(" (")); |
|
1393 desc.Zero(); |
|
1394 if (b.iFlags & RMemoryAccess::TBreakpointInfo::EPending) desc.Append(_L("PENDING ")); |
|
1395 if (!(b.iFlags & RMemoryAccess::TBreakpointInfo::EEnabled)) desc.Append(_L("DISABLED ")); |
|
1396 if (b.iFlags & RMemoryAccess::TBreakpointInfo::EHardware) desc.Append(_L("HARDWARE ")); |
|
1397 b.iCondition.Description(desc); |
|
1398 if (desc.Length() && desc[desc.Length()-1] == ' ') desc.SetLength(desc.Length()-1); |
|
1399 Write(desc); |
|
1400 if (brackets) Write(_L(")")); |
|
1401 Write(KCrLf); |
|
1402 } |
|
1403 } |
|
1404 else |
|
1405 { |
|
1406 Printf(_L("No breakpoints defined.\r\n")); |
|
1407 } |
|
1408 CleanupStack::PopAndDestroy(&buf); |
|
1409 } |
|
1410 |
|
1411 void CCmdFdb::CBreakpointNotifier::RunL() |
|
1412 { |
|
1413 if (iStatus.Int() < 0) |
|
1414 { |
|
1415 iCmd.PrintError(iStatus.Int(), _L("Error returned from NotifyBreakpoint")); |
|
1416 return; |
|
1417 } |
|
1418 |
|
1419 RMemoryAccess::TBreakpointNotification notif = iCmd.iBreakpointNotification; |
|
1420 iCmd.iMemAccess.NotifyBreakpoint(iCmd.iBreakpointNotificationPkg, iStatus); |
|
1421 SetActive(); |
|
1422 |
|
1423 iCmd.BreakpointHit(notif); |
|
1424 } |
|
1425 |
|
1426 void CCmdFdb::CBreakpointNotifier::DoCancel() |
|
1427 { |
|
1428 iCmd.iMemAccess.CancelNotifyBreakpoint(); |
|
1429 } |
|
1430 |
|
1431 CCmdFdb::CBreakpointNotifier::CBreakpointNotifier(CCmdFdb& aCmd) |
|
1432 : CActive(CActive::EPriorityStandard), iCmd(aCmd) |
|
1433 { |
|
1434 CActiveScheduler::Add(this); |
|
1435 iCmd.iMemAccess.NotifyBreakpoint(iCmd.iBreakpointNotificationPkg, iStatus); |
|
1436 SetActive(); |
|
1437 } |
|
1438 |
|
1439 CCmdFdb::CBreakpointNotifier::~CBreakpointNotifier() |
|
1440 { |
|
1441 Cancel(); |
|
1442 } |
|
1443 |
|
1444 void CCmdFdb::BreakpointHit(const RMemoryAccess::TBreakpointNotification& aNotif) |
|
1445 { |
|
1446 iLineEditor->RemovePromptAndUserInput(); |
|
1447 Printf(_L("Breakpoint %d hit in thread %u: "), aNotif.iBreakpointId, aNotif.iThreadId); |
|
1448 Write(LookupSymbol(aNotif.iAddress)); |
|
1449 Write(KCrLf); |
|
1450 if (iCurrent == NULL) |
|
1451 { |
|
1452 Printf(_L("(Attaching to thread %u)\r\n"), aNotif.iThreadId); |
|
1453 TRAP_IGNORE(AttachL(aNotif.iThreadId)); |
|
1454 } |
|
1455 iLineEditor->ReinstatePromptAndUserInput(); |
|
1456 } |
|
1457 |
|
1458 TInt CCmdFdb::BreakTest(TAny* /*aPtr*/) |
|
1459 { |
|
1460 //LtkUtils::Breakpoint(); |
|
1461 LtkUtils::RawPrint(_L8("Breaktest has completed\r\n")); |
|
1462 return 5; |
|
1463 } |
|
1464 |
|
1465 TInt CCmdFdb::BreakTestLtk(TAny* /*aPtr*/) |
|
1466 { |
|
1467 LtkUtils::Breakpoint(); |
|
1468 return 6; |
|
1469 } |
|
1470 |
|
1471 TInt CCmdFdb::BreakTestCond(TAny* /*aPtr*/) |
|
1472 { |
|
1473 CTrapCleanup* trap = CTrapCleanup::New(); |
|
1474 TRAPD(err, User::Leave(-1)); // This shouldn't trigger the break |
|
1475 TRAP(err, User::Leave(-5)); // This should |
|
1476 delete trap; |
|
1477 return 0; |
|
1478 } |
|
1479 |
|
1480 void CCmdFdb::ClearAllBreakpointsL() |
|
1481 { |
|
1482 RBuf8 buf; |
|
1483 CleanupClosePushL(buf); |
|
1484 buf.CreateL(1024); |
|
1485 LeaveIfErr(iMemAccess.GetBreakpoints(buf), _L("Couldn't read breakpoint information")); |
|
1486 RMemoryAccess::TBreakpointInfo* bs = (RMemoryAccess::TBreakpointInfo*)buf.Ptr(); |
|
1487 TInt count = buf.Length() / sizeof(RMemoryAccess::TBreakpointInfo); |
|
1488 for (TInt i = 0; i < count; i++) |
|
1489 { |
|
1490 RMemoryAccess::TBreakpointInfo& b = bs[i]; |
|
1491 TInt err = iMemAccess.ClearBreakpoint(b.iBreakpointId); |
|
1492 if (err) PrintWarning(_L("Couldn't clear breakpoint %d, err=%d"), b.iBreakpointId, err); |
|
1493 } |
|
1494 CleanupStack::PopAndDestroy(&buf); |
|
1495 } |
|
1496 |
|
1497 void CCmdFdb::CheckForConditionL(TLex& aLex, RMemoryAccess::TPredicate& aCondition) |
|
1498 { |
|
1499 aLex.SkipSpace(); |
|
1500 if (aLex.Eos()) return; |
|
1501 _LIT(KRegErr, "First argument in a conditional must be a register r0-r15"); |
|
1502 if (aLex.Get() != 'r') LeaveIfErr(KErrArgument, KRegErr); |
|
1503 TInt reg; |
|
1504 LeaveIfErr(aLex.Val(reg), KRegErr); |
|
1505 if (reg < 0 || reg > 15) LeaveIfErr(KErrArgument, KRegErr); |
|
1506 RMemoryAccess::TPredicate::TOp op = RMemoryAccess::TPredicate::ENothing; |
|
1507 TUint opchar = aLex.Get(); |
|
1508 switch (opchar) |
|
1509 { |
|
1510 case '<': |
|
1511 op = RMemoryAccess::TPredicate::ELt; |
|
1512 if (aLex.Peek() == '=') |
|
1513 { |
|
1514 op = RMemoryAccess::TPredicate::ELe; |
|
1515 aLex.Get(); |
|
1516 } |
|
1517 break; |
|
1518 case '>': |
|
1519 op = RMemoryAccess::TPredicate::EGt; |
|
1520 if (aLex.Peek() == '=') |
|
1521 { |
|
1522 op = RMemoryAccess::TPredicate::EGe; |
|
1523 aLex.Get(); |
|
1524 } |
|
1525 break; |
|
1526 case '!': |
|
1527 if (aLex.Get() != '=') LeaveIfErr(KErrArgument, _L("Unrecognised operand")); |
|
1528 op = RMemoryAccess::TPredicate::ENe; |
|
1529 break; |
|
1530 case '=': |
|
1531 op = RMemoryAccess::TPredicate::EEq; |
|
1532 if (aLex.Peek() == '=') aLex.Get(); // We allow == as well as = |
|
1533 break; |
|
1534 default: |
|
1535 LeaveIfErr(KErrArgument, _L("Unrecognised operand")); |
|
1536 break; |
|
1537 } |
|
1538 TInt val; |
|
1539 TInt err = LtkUtils::HexLex(aLex, (TUint&)val); |
|
1540 if (err) |
|
1541 { |
|
1542 // Try normal - this handles signed negative numbers, which HexLex doesn't afaik |
|
1543 err = aLex.Val(val); |
|
1544 } |
|
1545 LeaveIfErr(err, _L("Couldn't parse value")); |
|
1546 TBool signedCompare = ETrue; |
|
1547 if (aLex.Peek() == 'u' || aLex.Peek() == 'U') |
|
1548 { |
|
1549 aLex.Get(); |
|
1550 signedCompare = EFalse; |
|
1551 } |
|
1552 if (signedCompare) |
|
1553 { |
|
1554 op = (RMemoryAccess::TPredicate::TOp)(op + 6); // ELt -> ESignedLt etc |
|
1555 //Printf(_L("Op=%d reg=%d val=%d\r\n"), op, reg, val); |
|
1556 } |
|
1557 //else Printf(_L("Op=%d reg=%d val=%uu\r\n"), op, reg, (TUint)val); |
|
1558 if (aLex.Peek() == ',') |
|
1559 { |
|
1560 // Eat comma |
|
1561 aLex.Get(); |
|
1562 } |
|
1563 LeaveIfErr(aCondition.AddCondition(op, reg, (TUint32)val), _L("Couldn't add condition to TPredicate")); |
|
1564 } |