diff -r 000000000000 -r 7f656887cf89 commands/fdb/fdb.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commands/fdb/fdb.cpp Wed Jun 23 15:52:26 2010 +0100 @@ -0,0 +1,1564 @@ +// fdb.cpp +// +// Copyright (c) 2009 - 2010 Accenture. All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Accenture - Initial contribution +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include // Is this available in all environments? If not, need to define TDesType ourselves +#include +#include +_LIT(KCrLf, "\r\n"); +_LIT(KPrompt, "fdb>"); + +using namespace IoUtils; +using namespace LtkUtils; + +const TInt KMaxRegisters = 32; + +class CCmdFdb : public CMemoryAccessCommandBase, public MLineEditorObserver, public MLineCompleter + { +public: + static CCommandBase* NewLC(); + ~CCmdFdb(); +private: + struct SThreadContext; + CCmdFdb(); + void InstallL(); + void AttachL(TUint aThreadId); + void Detach(SThreadContext* aContext); + void PrintThreadInfo(SThreadContext& aContext); + void PrintRegistersL(SThreadContext& aThread); + void PrintRegistersL(SThreadContext& aThread, TBool aUserMode); + void PrintRegistersLRPC(SThreadContext& aThread, TBool aUserMode); + void RefreshL(SThreadContext& aContext); + void RefreshIfRunningL(SThreadContext& aContext); + TPtrC LookupSymbol(TUint32 aAddress); + void StartCommandParsingL(); + void ProcessLineL(const TDesC& aLine); + void FocusL(TUint aThreadId); + SThreadContext* ContextForThread(TUint aThreadId) const; + SThreadContext& CurrentL(); + SThreadContext& CurrentNoRefreshL(); + enum TPrintMode { EJustSymbols, EAllData, EHexDump }; + void PrintMemL(TUint aThreadId, TUint32 aStart, TUint32 aEnd, TPrintMode aMode); + void BrrrrrainsL(); + void ShowHelpTextL(); + TBool IsSymbol(TUint aAddress) const; + void StartInteractiveViewL(TLinAddr aAddress); + void DrawMemViewL(TBool aUpdate); + void UpdateMemForMemViewL(); + static TInt IsDescriptorHeader(TUint8 const* aHeaderPtr, TInt aMaxLen); + void ShowBreakpointsL(); + static TInt BreakTest(TAny* aPtr); + static TInt BreakTestLtk(TAny* aPtr); + static TInt BreakTestCond(TAny* aPtr); + void ClearAllBreakpointsL(); + void BreakpointHit(const RMemoryAccess::TBreakpointNotification& aNotif); + void CompleteLineWithSymbolL(TConsoleLine& aLine, TLex& aLex); + void CheckForConditionL(TLex& aLex, RMemoryAccess::TPredicate& aCondition); + +private: // From MLineCompleter + void LcCompleteLineL(TConsoleLine& aLine, const TChar& aEscapeChar); + +private: // From MLineEditorObserver + void LeoHandleLine(const TDesC& aLine); + +private: // From CCommandBase. + virtual const TDesC& Name() const; + virtual void DoRunL(); + virtual void ArgumentsL(RCommandArgumentList& aArguments); + virtual void OptionsL(RCommandOptionList& aOptions); + void RunL(); + void DoCancel(); + +private: + // Command line stuff + TBool iAllThreads; + TUint iThreadId; + TFileName2 iBsymFile; + TFileName2 iMapFileDir; + + // Thread context + struct SThreadContext + { + SThreadContext() : iUserValidRegisters(0), iSupervisorValidRegisters(0), iFlags(0) {} + + RThread iThread; + TUint32 iUserRegisters[KMaxRegisters]; + TUint32 iUserValidRegisters; + TUint32 iSupervisorRegisters[KMaxRegisters]; + TUint32 iSupervisorValidRegisters; + TThreadKernelInfo iKernelInfo; + enum TFlags { ERunning = 1, }; + TBitFlags32 iFlags; + }; + SThreadContext* iCurrent; + RPointerArray iThreads; + + // Other stuff + CSymbolics* iSymbols; + TBuf<256> iTempNameBuf; + + // Line editor support + TIoConsWriterAdaptor iConsoleAdapter; + CLineEditor* iLineEditor; + TBuf<256> iCommandLineBuf; + TBool iFinishedCommand; + TBool iShouldExit; + + // Interactive wossname support + enum TViewType + { + EUnspecified, + EStack, + EHeap, + }; + TViewType iMemoryViewType; + TLinAddr iMemStart; + RBuf8 iMemBuf; + /* + class TCrumb + { + enum TType { EAddress, EHeapCell, }; + TType iType; + TLinAddr iAddress; + TInt iIndex; + } + RArray iBreadcrumbs; + RArray iMarks; + RArray iLinks; + */ + + // Breakpoint support + RMemoryAccess::TBreakpointNotification iBreakpointNotification; + TPckg iBreakpointNotificationPkg; + class CBreakpointNotifier : public CActive + { + public: + CBreakpointNotifier(CCmdFdb& aCmd); + ~CBreakpointNotifier(); + + private: + void Request(); + void RunL(); + void DoCancel(); + + private: + CCmdFdb& iCmd; + }; + friend class CBreakpointNotifier; // Declaration needed for gcc 2.9 + CBreakpointNotifier* iBreakpointNotifier; + }; + +EXE_BOILER_PLATE(CCmdFdb) + +CCommandBase* CCmdFdb::NewLC() + { + CCmdFdb* self = new(ELeave) CCmdFdb(); + CleanupStack::PushL(self); + self->BaseConstructL(); + return self; + } + +CCmdFdb::~CCmdFdb() + { + Cancel(); + delete iLineEditor; + for (TInt i = 0; i < iThreads.Count(); i++) + { + SThreadContext* thread = iThreads[i]; + thread->iThread.Close(); + delete thread; + } + iThreads.Close(); + delete iSymbols; + iMemBuf.Close(); + delete iBreakpointNotifier; + /* + iLinks.Close(); + iBreadcrumbs.Close(); + iMarks.Close(); + */ + } + +CCmdFdb::CCmdFdb() + : CMemoryAccessCommandBase(EManualComplete), iConsoleAdapter(Stdout()), iBreakpointNotificationPkg(iBreakpointNotification) + { + } + +const TDesC& CCmdFdb::Name() const + { + _LIT(KName, "fdb"); + return KName; + } + +void CCmdFdb::ArgumentsL(RCommandArgumentList& aArguments) + { + aArguments.AppendUintL(iThreadId, _L("threadid")); + } + +void CCmdFdb::OptionsL(RCommandOptionList& aOptions) + { + aOptions.AppendBoolL(iAllThreads, _L("all")); + aOptions.AppendFileNameL(iBsymFile, _L("symbols")); + aOptions.AppendFileNameL(iMapFileDir, _L("mapfiles")); + } + +struct STestData + { + STestData() : iPtr8(NULL, 0) {} + + char iPad1[12]; + TBuf8<6> iBuf8; + TInt iPad2; + TBufC8<9> iBufC8; + char iPad3; + TPtrC8 iPtrC8; + TUint64 iPad4; + TPtr8 iPtr8; + char iPad5[3]; + }; + + +void CCmdFdb::DoRunL() + { + LoadMemoryAccessL(); + TInt mode = iMemAccess.GetZombieDebugMode(); + LeaveIfErr(mode, _L("Couldn't get zombie mode from driver")); + + iSymbols = new(ELeave) CSymbolics(FsL()); + if (iBsymFile.Length()) + { + //Printf(_L("Loading symbolics... ")); // It can take a while... + iSymbols->AddBsymFileL(iBsymFile); + //Printf(_L("done.\r\n")); + } + if (iMapFileDir.Length()) + { + iSymbols->SetFallbackMapFileDirL(iMapFileDir); + } + + // Testing + //CMapFile* map = CMapFile::NewL(FsL(), _L("C:\\symbols\\QResources3.exe.map")); + //iSymbols->AddMapFileL(map); + //const TDesC& symName = iSymbols->LookupL(_L("QResources3.exe"), 0x10abc); + //Printf(_L("Found symbol = %S\r\n"), &symName); + + if (Env().IsDefined(_L("FDB_TEST"))) + { + STestData test; + test.iBuf8 = _L8("12345"); + test.iBufC8 = _L8("abcdefg"); + test.iPtrC8.Set(test.iBufC8); + TPckg pkg(test); + iMemBuf.Assign(pkg.AllocL()); + DrawMemViewL(EFalse); + return; + } + + TBool install = ETrue; + // Figure out something sensible to do + if (mode != 0 || iThreadId != 0) install = EFalse; + + if (iAllThreads) Printf(_L("Note that because --all-threads was specified, this and all future commands will hang rather than exiting!\r\n")); + + InstallL(); // Always do this, in case user has changed the --all-threads setting + if (install) + { + // This will really mess up evalid, but the toolkit doesn't really use it so I don't feel *too* guilty + 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")); + } + else + { + AttachL(iThreadId); + } + + iBreakpointNotifier = new(ELeave) CBreakpointNotifier(*this); + TInt err = iMemAccess.RegisterPersistantBreakpoint(LtkUtils::BreakpointAddr()); + if (err != KErrNone && err != KErrAlreadyExists) + { + LeaveIfErr(err, _L("Couldn't install the LtkUtils::Breakpoint() breakpoint")); + } + StartCommandParsingL(); + } + +void CCmdFdb::InstallL() + { + LeaveIfErr(iMemAccess.SetZombieDebugMode(iAllThreads ? 2 : 1), _L("Couldn't install thread monitor")); + const TUint32 KAllThreadsSystem = 1; // Word 2, bit 0 + TUint32 dbgmask = UserSvr::DebugMask(2); + if (dbgmask & KAllThreadsSystem) + { + Printf(_L("(Clearing KAllThreadsSystem attribute)\r\n")); + User::SetDebugMask(dbgmask & ~KAllThreadsSystem, 2); + } + } + +void CCmdFdb::AttachL(TUint aThreadId) + { + if (aThreadId == 0) + { + // Need to figure out something to attach to + TBuf8<64> buf; // TODO handle more than this many zombies + TInt res = iMemAccess.GetZombies(buf); + LeaveIfErr(res, _L("Couldn't get zombie info from driver")); + //TInt leftOver = 0; + //if (res > buf.MaxLength()) leftOver = (res - buf.MaxLength()) / sizeof(TUint); + RMemoryAccess::TZombieInfo* ptr = (RMemoryAccess::TZombieInfo*)buf.Ptr(); + TInt zombiecount = buf.Length() / sizeof(RMemoryAccess::TZombieInfo); + for (TInt i = 0; i < zombiecount; i++) + { + TUint id = ptr[i].iThreadId; + if (ContextForThread(id)) continue; // already attached to this thread, keep looking + RThread thread; + TInt err = iMemAccess.RThreadForceOpen(thread, id); + if (err) + { + PrintWarning(_L("Couldn't open thread %u"), id); + continue; + } + iTempNameBuf = thread.FullName(); + Printf(_L("(Found thread id %u %S)\r\n"), id, &iTempNameBuf); + if (aThreadId == 0) aThreadId = id; + thread.Close(); + } + if (aThreadId == 0) + { + if (buf.Length()) + Printf(_L("No zombied threads found that aren't already attached.\r\n")); + else + PrintWarning(_L("No zombied threads found to attach to.")); + return; + } + else + { + Printf(_L("(Attaching to thread %u)\r\n"), aThreadId); + } + } + + SThreadContext* context = ContextForThread(aThreadId); + if (context) + { + PrintWarning(_L("Already attached to thread %u, focussing it instead."), aThreadId); + iCurrent = context; + return; + } + + context = new(ELeave) SThreadContext; + CleanupStack::PushL(context); + RThread& thread(context->iThread); + CleanupClosePushL(thread); + LeaveIfErr(iMemAccess.RThreadForceOpen(thread, aThreadId), _L("Couldn't open thread %u"), aThreadId); + RefreshL(*context); + iThreads.AppendL(context); + CleanupStack::Pop(2, context); // context->iThread, context + iCurrent = context; + + PrintThreadInfo(*context); + // Don't print any more than this - it makes the output too wordy + //PrintRegistersL(*context); + } + +void CCmdFdb::PrintThreadInfo(SThreadContext& aContext) + { + RThread& aThread = aContext.iThread; + TFullName name = aThread.FullName(); + Write(_L("Name: ")); + Write(name); + + Printf(_L("\r\nThread Id: %d, exit type: "), (TUint)aThread.Id()); + TExitType exitType = aThread.ExitType(); + if (exitType == EExitPending) + { + Printf(_L("Still running\r\n")); + } + else if (exitType == EExitKill) + { + Printf(_L("Kill %d\r\n"), aThread.ExitReason()); + } + else if (exitType == EExitTerminate) + { + Printf(_L("Terminate %d\r\n"), aThread.ExitReason()); + } + else if (exitType == EExitPanic) + { + TExitCategoryName exitCategory = aThread.ExitCategory(); + Printf(_L("Panic %S %d\r\n"), &exitCategory, aThread.ExitReason()); + } + + Printf(_L("User stack: base=%08x limit=%08x\r\n"), aContext.iKernelInfo.UserStackBase(), aContext.iKernelInfo.iUserStackLimit); + Printf(_L("Kern stack: base=%08x limit=%08x\r\n"), aContext.iKernelInfo.iSupervisorStack + aContext.iKernelInfo.iSupervisorStackSize, aContext.iKernelInfo.iSupervisorStack); + } + +void CCmdFdb::PrintRegistersL(SThreadContext& aThread) + { + PrintRegistersL(aThread, ETrue); + PrintRegistersL(aThread, EFalse); + } + +void CCmdFdb::PrintRegistersL(SThreadContext& aThread, TBool aUserMode) + { + if (aUserMode) + { + Printf(_L("User mode registers:\r\n")); + } + else + { + Printf(_L("Supervisor mode registers:\r\n")); + } + + TUint32 valid = aThread.iUserValidRegisters; + TUint32* ptr = aThread.iUserRegisters; + if (!aUserMode) + { + valid = aThread.iSupervisorValidRegisters; + ptr = aThread.iSupervisorRegisters; + } + + if (valid == 0) Printf(_L("(No valid registers)\r\n")); + // From here down is ARM specific, but since no other platforms implement this API there's no pressing need to ifdef it + // See TArmRegSet for the ordering + for (TInt i = 0; i < 16; i++) + { + if (valid & (1<iFlags.IsSet(SThreadContext::ERunning)) + { + // Thread is still running, we should refresh our info + RefreshL(aContext); + } + } + +void CCmdFdb::RefreshL(SThreadContext& aContext) + { + aContext.iFlags.Assign(SThreadContext::ERunning, aContext.iThread.ExitType() == EExitPending); + TUint tid = aContext.iThread.Id(); + + // Get registers + TPtr8 userreg((TUint8*)&aContext.iUserRegisters[0], KMaxRegisters*sizeof(TUint32), KMaxRegisters*sizeof(TUint32)); + userreg.FillZ(); + aContext.iUserValidRegisters = 0; + TInt err = iMemAccess.GetRegisters(aContext.iThread, ETrue, userreg, aContext.iUserValidRegisters); + if (err) PrintError(err, _L("Couldn't read user registers for thread %u"), tid); + + TPtr8 supreg((TUint8*)&aContext.iSupervisorRegisters[0], KMaxRegisters*sizeof(TUint32), KMaxRegisters*sizeof(TUint32)); + supreg.FillZ(); + aContext.iSupervisorValidRegisters = 0; + err = iMemAccess.GetRegisters(aContext.iThread, EFalse, supreg, aContext.iSupervisorValidRegisters); + if (err) PrintError(err, _L("Couldn't read supervisor registers for thread %u\r\n"), tid); + + // And memaccess info + TPckg kerninfo(aContext.iKernelInfo); + err = iMemAccess.GetObjectInfoByHandle(EThread, RThread().Id(), aContext.iThread.Handle(), kerninfo); + if (err) PrintError(err, _L("Couldn't read thread info from memoryaccess for thread %u\r\n"), tid); + //aContext.iFlags.Assign(SThreadContext::ESuspended, aContext.iKernelInfo.iNThreadSuspendCount != 0); + } + +TPtrC CCmdFdb::LookupSymbol(TUint32 aAddress) + { + TPtrC name; + if (iSymbols) + { + // Try straight ROM address lookup + TRAPD(err, name.Set(iSymbols->LookupL(aAddress))); + if (err) PrintError(err, _L("Failed to lookup symbol")); + if (name.Length()) return name; + } + // Try getting a codeseg from memaccess + TFullName8 codesegname; + TInt res = iMemAccess.FindAddressInCodeSegments(codesegname, (TAny*)aAddress); + if (res >= 0) + { + iTempNameBuf.Copy(codesegname); + if (iSymbols) + { + // Try codeseg lookup in CSymbolics (ie in a CMapFile) + TParsePtrC parse(iTempNameBuf); + TRAPD(err, name.Set(iSymbols->LookupL(parse.NameAndExt(), res))); + if (err) PrintError(err, _L("Failed to lookup symbol")); + if (name.Length()) return name; + } + // Otherwise fallback to just doing codeseg+offset + iTempNameBuf.AppendFormat(_L(" + 0x%x"), res); + return iTempNameBuf; + } + // Last ditch, check if euser thinks it's in ROM + if (RFs::IsRomAddress((TAny*)aAddress)) + { + _LIT(KSomewhere, "[Somewhere in ROM]"); + return KSomewhere(); + } + return KNullDesC(); + } + +void CCmdFdb::StartCommandParsingL() + { + TBuf<64> historyFile; + User::LeaveIfError(FsL().PrivatePath(historyFile)); + historyFile.Insert(0, _L("c:")); + historyFile.Append(_L("fdb_history")); + Fs().CreatePrivatePath(EDriveC); + + iLineEditor = CLineEditor::NewL(Fs(), iConsoleAdapter, *this, *this, historyFile); + iLineEditor->Start(KPrompt, iCommandLineBuf); + iLineEditor->ReinstatePromptAndUserInput(); + SetErrorReported(EFalse); + Stdin().WaitForKey(iStatus); + SetActive(); + } + +void CCmdFdb::RunL() + { + if (iStatus.Int() < 0) + { + // iosrv dead? + Complete(iStatus.Int()); + return; + } + + iLineEditor->HandleKey(Stdin().KeyCode(), Stdin().KeyModifiers()); + if (iShouldExit) + { + Complete(KErrNone); + } + else if (iFinishedCommand) + { + iLineEditor->Start(KPrompt, iCommandLineBuf); + iLineEditor->ReinstatePromptAndUserInput(); + iFinishedCommand = EFalse; + SetErrorReported(EFalse); // We need to clear this each time through + } + + if (!iShouldExit) + { + Stdin().WaitForKey(iStatus); + SetActive(); + } + } + +void CCmdFdb::DoCancel() + { + Stdin().WaitForKeyCancel(); + } + +void CCmdFdb::LcCompleteLineL(TConsoleLine& aLine, const TChar& /*aEscapeChar*/) + { + TPtrC line = aLine.ContentsToCursor(); + TLex lex(line); + TPtrC cmd = lex.NextToken(); + if (cmd != _L("b") && cmd != _L("break")) return; // We only currently do completion on the 'break' command + + CompleteLineWithSymbolL(aLine, lex); + } + +void CCmdFdb::CompleteLineWithSymbolL(TConsoleLine& aLine, TLex& aLex) + { + // Have we got as far as a codeseg? We don't support completing on codeseg yet + TPtrC codeseg = aLex.NextToken(); + TPtrC ext = codeseg.Right(4); + if (ext != _L(".dll") && ext != _L(".exe")) return; + + aLex.SkipSpace(); + TInt symbolStartPos = aLex.Offset(); + TPtrC symbol = aLex.Remainder(); + RLtkBuf buf; + buf.CreateLC(512); + buf.Copy(symbol); + CDesC16Array* suggestions = new(ELeave) CDesC16ArrayFlat(32); + CleanupStack::PushL(suggestions); + //TODO this can be slow first time, need some UI to warn the user + iSymbols->CompleteL(codeseg, buf, *suggestions); + if (buf.Length() > symbol.Length()) + { + aLine.Replace(symbolStartPos, buf); + } + // If the tab added any chars we don't show choices + else if (suggestions->Count() > 1) + { + buf.Zero(); + for (TInt i = 0; i < suggestions->Count(); i++) + { + if (i > 0) buf.AppendL('\t'); + buf.AppendL((*suggestions)[i]); + } + aLine.PrintCompletionPossibilitiesL(buf); + } + + CleanupStack::PopAndDestroy(suggestions); + CleanupStack::PopAndDestroy(&buf); + } + +void CCmdFdb::LeoHandleLine(const TDesC& aLine) + { + iFinishedCommand = ETrue; + if (aLine.Length()) + { + TRAPD(err, ProcessLineL(aLine)); + if (err) + { + PrintError(err, _L("Error executing command")); + } + } + } + +void CCmdFdb::ProcessLineL(const TDesC& aLine) + { + TLex lex(aLine); + TPtrC cmd = lex.NextToken(); + lex.SkipSpace(); + + char ch = 0; + if (cmd.Length() == 1) + { + ch = cmd[0]; + } + + _LIT(KHelp, "help"); + _LIT(KExit, "exit"); + _LIT(KAttach, "attach"); + _LIT(KDetach, "detach"); + _LIT(KFocus, "focus"); + _LIT(KRegisters, "registers"); + _LIT(KLookup, "lookup"); + _LIT(KStack, "stack"); + _LIT(KKstack, "kstack"); + _LIT(KList, "list"); + _LIT(KMem, "mem"); + _LIT(KUninstall, "uninstall"); + _LIT(KBrowse, "browse"); + _LIT(KBreak, "break"); + _LIT(KContinue, "continue"); + _LIT(KClear, "clear"); + _LIT(KLoad, "load"); + if (cmd == KHelp || ch == 'h') + { + ShowHelpTextL(); + } + else if (cmd == KExit || ch == 'x') + { + iShouldExit = ETrue; // TODO check whether a detach is desired + } + else if (cmd == KUninstall || ch == 'u') + { + Printf(_L("Uninstalling thread hook, clearing all breakpoints, freeing all zombie threads. Undertakers will now get notified of the threads' exits.\r\n")); + iMemAccess.SetZombieDebugMode(0); + iShouldExit = ETrue; + } + else if (cmd == KAttach || ch == 'a') + { + TUint threadId = 0; + lex.Val(threadId); + AttachL(threadId); + } + else if (cmd == KFocus || ch == 'f') + { + TUint threadId = 0; + lex.Val(threadId); + if (threadId == 0) + { + PrintThreadInfo(CurrentL()); + } + else + { + FocusL(threadId); + } + } + else if (cmd == KRegisters || ch == 'r') + { + PrintRegistersL(CurrentL()); + } + else if (cmd == KLookup || ch == '?') + { + TUint32 addr = LtkUtils::HexLexL(lex); + Printf(_L("%08x: "), addr); + Write(LookupSymbol(addr)); + Write(KCrLf); + } + else if (cmd == KDetach || ch == 'd') + { + TUint threadId = 0; + lex.Val(threadId); + SThreadContext* thread = NULL; + if (threadId == 0) + { + // No point refreshing, all it will do is possibly complain about reading the registers (which looks confusing) + thread = &CurrentNoRefreshL(); + } + else + { + thread = ContextForThread(threadId); + if (!thread) LeaveIfErr(KErrNotFound, _L("Thread %u is not currently attached."), threadId); + } + Detach(thread); + } + else if (cmd == KStack || ch == 't') + { + TBool all = lex.NextToken() == _L("all"); + SThreadContext& c = CurrentL(); + TUint32 start = c.iKernelInfo.iUserStackLimit; + TUint32 end = c.iKernelInfo.UserStackBase(); + if (start == 0) + { + Printf(_L("No user stack for this thread.\r\n")); + } + else + { + PrintRegistersLRPC(c, ETrue); + if ((c.iUserValidRegisters & (1 << 13)) && Rng(start, c.iUserRegisters[13], end)) start = c.iUserRegisters[13]; + PrintMemL(c.iThread.Id(), start, end, all ? EAllData : EJustSymbols); + } + } + else if (cmd == KKstack || ch == 'k') + { + TBool all = lex.NextToken() == _L("all"); + SThreadContext& c = CurrentL(); + TUint32 start = c.iKernelInfo.iSupervisorStack; + TUint32 end = start + c.iKernelInfo.iSupervisorStackSize; + if (start == 0) + { + Printf(_L("Couldn't find kernel stack for this thread (!?).\r\n")); + } + else + { + PrintRegistersLRPC(c, EFalse); + if ((c.iSupervisorValidRegisters & (1 << 13)) && Rng(start, c.iSupervisorRegisters[13], end)) start = c.iSupervisorRegisters[13]; + 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 + } + } + else if (cmd == KList || ch == 'l') + { + BrrrrrainsL(); + } + else if (cmd == KMem || ch == 'm') + { + TUint32 start = LtkUtils::HexLexL(lex); + lex.SkipSpace(); + TUint32 len = LtkUtils::HexLexL(lex); + PrintMemL(CurrentL().iThread.Id(), start, start+len, EHexDump); + } + else if (cmd == KBrowse) + { + TPtrC remainder = lex.Remainder(); + TLinAddr addr = 0; + if (remainder.Length() == 0 || remainder == _L("heap")) + { + //TODO + } + else if (remainder == _L("stack")) + { + //TODO + } + else + { + addr = LtkUtils::HexLexL(lex); + } + StartInteractiveViewL(addr); + } + else if (cmd == KBreak || ch == 'b') + { + TInt res = KErrNone; + TBool set = ETrue; + TPtrC remainder = lex.Remainder(); + if (remainder.Length() == 0) + { + ShowBreakpointsL(); + set = EFalse; + } + else if (remainder == _L("test")) + { + // This is undocumented, for testing only + RThread me; + res = iMemAccess.SetBreakpoint(me, (TLinAddr)&LtkUtils::RawPrint); + LeaveIfErr(res, _L("Couldn't set test breakpoint")); + RThread testThread; + LeaveIfErr(testThread.Create(_L("BreakpointTestThread"), &BreakTest, 8192, NULL, NULL), _L("Couldn't create test thread")); + testThread.Resume(); + testThread.Close(); + } + else if (remainder == _L("testltk")) + { + // This is undocumented, for testing only + RThread testThread; + LeaveIfErr(testThread.Create(_L("BreakpointLtkTestThread"), &BreakTestLtk, 8192, NULL, NULL), _L("Couldn't create test thread")); + testThread.Resume(); + testThread.Close(); + set = EFalse; + } + else if (remainder == _L("testhw")) + { + // This is undocumented, for testing only + RThread testThread; + LeaveIfErr(testThread.Create(_L("BreakpointTestThread"), &BreakTest, 8192, NULL, NULL), _L("Couldn't create test thread")); + res = iMemAccess.SetBreakpoint(testThread, (TLinAddr)&LtkUtils::RawPrint); + LeaveIfErr(res, _L("Couldn't set test breakpoint")); + testThread.Resume(); + testThread.Close(); + } + else if (remainder == _L("testcond")) + { + // This is undocumented, for testing only + RThread testThread; + LeaveIfErr(testThread.Create(_L("BreakpointTestThread"), &BreakTestCond, 8192, NULL, NULL), _L("Couldn't create test thread")); + RMemoryAccess::TPredicate condition; + LeaveIfErr(condition.AddCondition(RMemoryAccess::TPredicate::ESignedEq, 0, (TUint)-5), _L("Couldn't add condition")); + res = iMemAccess.SetBreakpoint(testThread, (TLinAddr)&User::Leave, &condition); + LeaveIfErr(res, _L("Couldn't set test breakpoint")); + testThread.Resume(); + testThread.Close(); + } + else + { + TUint addr; + TInt err = HexLex(lex, addr); + if (err) + { + // Try codeseg and symbol name + TPtrC codeseg = lex.NextToken(); + lex.SkipSpace(); + TPtrC name = lex.Remainder(); + // 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. + TInt space = name.LocateReverse(' '); + RMemoryAccess::TPredicate condition; + if (space >= 0 && space+1 < name.Length() && name[space+1] == 'r') + { + name.Set(name.Left(space)); + lex.Inc(name.Length()); + while (!lex.Eos()) + { + CheckForConditionL(lex, condition); + } + } + + TUint offset = 0; + TRAPL(offset = iSymbols->CodesegOffsetFromSymbolNameL(codeseg, name), _L("Couldn't find offset of symbol '%S' in codeseg '%S'"), &name, &codeseg); + RLtkBuf8 codeseg8; + CleanupClosePushL(codeseg8); + codeseg8.AppendL(codeseg); + res = iMemAccess.SetSymbolicBreakpoint(CurrentL().iThread, codeseg8, offset, &condition); + LeaveIfErr(res, _L("Couldn't create symbolic breakpoint")); + CleanupStack::PopAndDestroy(&codeseg8); + } + else + { + RMemoryAccess::TPredicate condition; + while (!lex.Eos()) + { + CheckForConditionL(lex, condition); + } + res = iMemAccess.SetBreakpoint(CurrentL().iThread, addr, &condition); + LeaveIfErr(res, _L("Couldn't create breakpoint")); + Printf(_L("Breakpoint created at 0x%08x "), addr); + Write(LookupSymbol(addr)); + Write(KCrLf); + } + } + + if (set) + { + if (res & RMemoryAccess::TBreakpointInfo::EHardware) + { + Printf(_L("Hardware breakpoint %d set.\r\n"), res & ~RMemoryAccess::TBreakpointInfo::EHardware); + } + else if (res == 0) + { + Printf(_L("Pending breakpoint set (Note these don't work yet!)\r\n")); + } + else + { + Printf(_L("Breakpoint %d set.\r\n"), res); + } + } + } + else if (cmd == KContinue || ch =='c') + { + TInt err = iMemAccess.ContinueFromBreakpoint(CurrentL().iThread); + LeaveIfErr(err, _L("Couldn't continue - is the thread definitely stopped on a breakpoint?")); + } + else if (cmd == KClear) + { + TPtrC remainder = lex.Remainder(); + if (remainder.Length() == 0) + { + ClearAllBreakpointsL(); + } + else + { + TInt n; + User::LeaveIfError(lex.Val(n)); + LeaveIfErr(iMemAccess.ClearBreakpoint(n), _L("Couldn't clear breakpoint %d"), n); + } + } + else if (cmd == KLoad) + { + TPtrC path = lex.Remainder(); + if (path.Right(5).CompareF(_L(".bsym")) == 0) + { + //Printf(_L("Loading symbolics... ")); // It can take a while... + iSymbols->AddBsymFileL(path); + //Printf(_L("done.\r\n")); + } + else + { + iSymbols->SetFallbackMapFileDirL(path); + } + } + else + { + PrintError(KErrNotFound, _L("Unrecognised command '%S'. Try 'help'."), &cmd); + } + } + +CCmdFdb::SThreadContext* CCmdFdb::ContextForThread(TUint aThreadId) const + { + for (TInt i = 0; i < iThreads.Count(); i++) + { + if ((TUint)iThreads[i]->iThread.Id() == aThreadId) + { + return iThreads[i]; + } + } + return NULL; + } + +CCmdFdb::SThreadContext& CCmdFdb::CurrentL() + { + CCmdFdb::SThreadContext& current = CurrentNoRefreshL(); + RefreshIfRunningL(current); + return current; + } + +CCmdFdb::SThreadContext& CCmdFdb::CurrentNoRefreshL() + { + if (!iCurrent) + { + LeaveIfErr(KErrNotReady, _L("No currently focussed thread")); + } + return *iCurrent; + } + +void CCmdFdb::FocusL(TUint aThreadId) + { + SThreadContext* c = ContextForThread(aThreadId); + if (c) iCurrent = c; + else + { + LeaveIfErr(KErrNotFound, _L("Couldn't find thread id %u in the attached threads. Do you need to do 'attach %u'?"), aThreadId, aThreadId); + } + } + +void CCmdFdb::Detach(SThreadContext* aContext) + { + TInt err = iMemAccess.ReleaseZombie(aContext->iThread); + if (err && aContext->iFlags.IsSet(SThreadContext::ERunning)) + { + // Don't complain about driver if the thread wasn't actually zombied + PrintError(err, _L("Driver couldn't find zombie thread")); + } + + TInt arrayPos = iThreads.Find(aContext); + iThreads.Remove(arrayPos); + if (iCurrent == aContext) iCurrent = NULL; + aContext->iThread.Close(); + delete aContext; + } + +void CCmdFdb::PrintMemL(TUint aThreadId, TUint32 aStart, TUint32 aEnd, TPrintMode aMode) + { + // word-align start and end + aStart &= ~3; + aEnd = (aEnd+3) & (~3); + + TInt size = aEnd - aStart; + RBuf8 mem; + CleanupClosePushL(mem); + mem.CreateL(size); + + TThreadMemoryAccessParamsBuf params; + params().iId = aThreadId; + params().iAddr = (TUint8*)aStart; + params().iSize = size; + + TInt err = iMemAccess.GetThreadMem(params, mem); + LeaveIfErr(err, _L("Couldn't read thread memory %08x-%08x"), aStart, aEnd); + + if (aMode == EHexDump) + { + TInt offset = (TInt)aStart; + LtkUtils::HexDumpToOutput(mem, Stdout(), offset); + } + else + { + const TUint32* ptr = (const TUint32*)mem.Ptr(); + const TInt count = mem.Size() / 4; + for (TInt i = 0; i < count; i++) + { + TUint32 word = ptr[i]; + TBool print = aMode == EAllData || IsSymbol(word); + if (print) + { + Printf(_L("%08x: %08x "), aStart + i*4, word); + Write(LookupSymbol(word)); + Write(KCrLf); + } + } + } + CleanupStack::PopAndDestroy(&mem); + } + +void CCmdFdb::BrrrrrainsL() + { + RBuf8 buf; + CleanupClosePushL(buf); + buf.CreateL(1024); + TInt res = iMemAccess.GetZombies(buf); + LeaveIfErr(res, _L("Couldn't get zombie info from driver")); + RMemoryAccess::TZombieInfo* ptr = (RMemoryAccess::TZombieInfo*)buf.Ptr(); + const TInt zombiecount = buf.Length()/sizeof(RMemoryAccess::TZombieInfo); + + // Go through all the zombies + for (TInt i = 0; i < zombiecount; i++) + { + TUint id = ptr[i].iThreadId; + SThreadContext* context = ContextForThread(id); + char stat = '-'; + char suspended = '-'; + if (context) + { + stat = 'a'; + if (context == iCurrent) stat = '*'; + iTempNameBuf = context->iThread.FullName(); + } + else + { + RThread thread; + TInt err = iMemAccess.RThreadForceOpen(thread, id); + if (err) + { + PrintWarning(_L("Couldn't open thread %u"), id); + continue; + } + iTempNameBuf = thread.FullName(); + thread.Close(); + } + if (ptr[i].iFlags & RMemoryAccess::TZombieInfo::ESuspended) suspended = 's'; + if (ptr[i].iFlags & RMemoryAccess::TZombieInfo::EBreakpoint) suspended = 'b'; + + Printf(_L("%c%c %u "), stat, suspended, id); + Write(iTempNameBuf); + Write(KCrLf); + } + + // Now do any attached threads that aren't zombied + for (TInt i = 0; i < iThreads.Count(); i++) + { + SThreadContext* c = iThreads[i]; + TUint id = (TUint)c->iThread.Id(); + RMemoryAccess::TZombieInfo dummy; dummy.iThreadId = id; + TBool foundInZombies = EFalse; + if (zombiecount) foundInZombies = RArray(sizeof(RMemoryAccess::TZombieInfo), ptr, zombiecount).Find(dummy) != KErrNotFound; + if (!foundInZombies) + { + char stat = 'a'; + if (c == iCurrent) stat = '*'; + iTempNameBuf = c->iThread.FullName(); + Printf(_L("%c- %u "), stat, id); + Write(iTempNameBuf); + Write(KCrLf); + } + } + + CleanupStack::PopAndDestroy(&buf); + } + +void CCmdFdb::ShowHelpTextL() + { + // Possible TODOs: hEap, Save, Ymodem? + _LIT(KStartOfCommands, "SUPPORTED COMMANDS\r\n\r\n"); + CTextBuffer* helpText = const_cast(GetHelpTextL()); + TInt found = helpText->Descriptor().Find(KStartOfCommands); + helpText->Delete(0, found + KStartOfCommands().Length()); + helpText->Write(Stdout()); + delete helpText; + } + +TBool CCmdFdb::IsSymbol(TUint aAddress) const + { + // Ranges probably not perfect, seem to be roughly ok though + TBool okRange = Rng(0x70000000u, aAddress, 0xA0000000u) || Rng(0xC0000000u, aAddress, 0xFC000000u); + return okRange && aAddress != 0xDEDEDEDE && aAddress != 0xAAAAAAAA && aAddress != 0xBBBBBBBB && aAddress != 0xCCCCCCCC; + } + +void CCmdFdb::StartInteractiveViewL(TLinAddr aAddress) + { + iMemStart = aAddress & (~3); + if (iMemStart >= iCurrent->iKernelInfo.iUserStackLimit && iMemStart <= iCurrent->iKernelInfo.UserStackBase()) + { + iMemoryViewType = EStack; + } + else //TODO EHeap + { + iMemoryViewType = EUnspecified; + } + + iMemBuf.Zero(); + TSize consoleSize(80,24); + Stdout().GetScreenSize(consoleSize); + iMemBuf.ReAllocL(consoleSize.iHeight * 16); // At most we display 16 bytes per line + TInt numBytesOnLine = 16; // By default + if (iMemoryViewType == EStack) numBytesOnLine = 4; + TInt numBytesOnScreen = numBytesOnLine * consoleSize.iHeight; + + TLinAddr lastAddr = 0; + for (;;) + { + iMemStart &= ~3; // Just checking + TBool update = (iMemStart != lastAddr); + lastAddr = iMemStart; + DrawMemViewL(update); + TUint key = Stdin().ReadKey(); + switch (key) + { + case EKeyPageUp: + iMemStart -= numBytesOnScreen; + break; + case EKeyPageDown: + iMemStart += numBytesOnScreen; + break; + case EKeyUpArrow: + iMemStart -= numBytesOnLine; + break; + case EKeyDownArrow: + iMemStart += numBytesOnLine; + break; + case 'q': + case 'x': + case EKeyEscape: + //Stdout().SetAttributesL(ConsoleAttributes::ENone); + return; + default: + break; + } + } + } + +void CCmdFdb::UpdateMemForMemViewL() + { + iMemBuf.Zero(); + + TThreadMemoryAccessParamsBuf params; + params().iId = iCurrent->iThread.Id(); + params().iAddr = (TUint8*)iMemStart; + params().iSize = iMemBuf.MaxSize(); + + TInt err = iMemAccess.GetThreadMem(params, iMemBuf); + if (err) PrintError(err, _L("Couldn't read thread memory %08x-%08x"), params().iAddr, params().iAddr+params().iSize); + } + +/* +class TFocusable + { +public: + TLinAddr iAddr; + enum TType + { + EHeapPtr, + } + } +*/ + +void CCmdFdb::DrawMemViewL(TBool aUpdate) + { + Stdout().ClearScreen(); + if (aUpdate) UpdateMemForMemViewL(); + + + // This is a really messy function. Can't seem to find a way of making it easier to read. + const ConsoleAttributes::TAttributes KNormal(ConsoleAttributes::ENone, ConsoleAttributes::EBlack, ConsoleAttributes::EWhite); + const ConsoleAttributes::TAttributes KSymbol(0, ConsoleAttributes::ERed, ConsoleAttributes::EUnchanged); + const ConsoleAttributes::TAttributes KDescriptor(0, ConsoleAttributes::EUnchanged, ConsoleAttributes::EYellow); + //const ConsoleAttributes::TAttributes KHeap(0, ConsoleAttributes::EUnchanged, ConsoleAttributes::ECyan); + //const ConsoleAttributes::TAttributes KHeapAlternate(0, ConsoleAttributes::EUnchanged, ConsoleAttributes::EBlue); + enum { ENoColor = 0, ESymbol = 1, EDescriptor = 2 }; + + TSize consoleSize(80,24); + Stdout().GetScreenSize(consoleSize); + TUint8 const*const bptr = iMemBuf.Ptr(); + TUint32 const*const ptr = (TUint32 const*)bptr; + //iLinks.Reset(); + CTextBuffer* text = CTextBuffer::NewLC(1024); + text->SetAttributesL(KNormal); + + TInt numBytesOnLine = 16; // By default + if (iMemoryViewType == EStack) + { + numBytesOnLine = 4; + } + //TInt numBytesOnScreen = numBytesOnLine * consoleSize.iHeight; + TInt remainingInDescriptor = 0; // Not in descriptor initially + //TInt remainingInHeapCell = 0; // Not worrying about heap cells right now + + for (TInt line = 0; line < consoleSize.iHeight - 1 && line*numBytesOnLine < iMemBuf.Length(); line++) + { + TBuf8<16> colorBuf; colorBuf.SetLength(colorBuf.MaxLength()); + TUint8* colorBufPtr = (TUint8*)colorBuf.Ptr(); + Mem::Fill(colorBufPtr, 16, ENoColor); + text->AppendFormatL(_L("%08x: "), iMemStart + line*numBytesOnLine); + + const TInt idxForLine = line * numBytesOnLine; + TInt i; + for (i = 0; i < numBytesOnLine; i += 4, colorBufPtr += 4) + { + const TInt idxInBuf = idxForLine + i; + if (idxInBuf >= iMemBuf.Length()) break; + + if (remainingInDescriptor == 0) remainingInDescriptor = IsDescriptorHeader(bptr + idxInBuf, 256); + TInt runLen = 0; + if (remainingInDescriptor > 0) + { + text->SetAttributesL(KDescriptor); + runLen = Min(remainingInDescriptor, numBytesOnLine-i); + if (runLen > 4) runLen = 4; // We only do up to 4 bytes at a time + Mem::Fill(colorBufPtr, runLen, EDescriptor); + } + + // Check for symbols + TUint32 word = ptr[idxInBuf / 4]; + if (IsSymbol(word)) + { + text->SetAttributesL(KSymbol); + colorBufPtr[0] |= ESymbol; + colorBufPtr[1] |= ESymbol; + colorBufPtr[2] |= ESymbol; + colorBufPtr[3] |= ESymbol; + if (iMemoryViewType == EStack) + { + TPtrC symb = LookupSymbol(word); + text->AppendFormatL(_L("%08x %S"), word, &symb); + text->SetAttributesL(KNormal); + text->AppendL(KCrLf); + continue; + } + } + + // Actually print the hex bytes + for (TInt ch = 0; ch < 4; ch++) + { + text->AppendFormatL(_L("%02X"), bptr[idxInBuf+ch]); + if (runLen) + { + remainingInDescriptor--; + runLen--; + if (remainingInDescriptor == 0) text->SetAttributesL(KNormal); // If we've just finished a run we clear the formatting before the space + } + text->AppendL(' '); + } + if (((i+4) % 16) == 0) text->SetAttributesL(KNormal); // In preparation for printing the ascii + if (((i+4) % 8) == 0) text->AppendL(' '); // Extra space every 8th char + } + + TInt rem = numBytesOnLine - i; + if (rem > 0) + { + // Need to fill in spaces for the hex bytes we don't have + _LIT(K3Space, " "); + while (rem--) text->AppendL(K3Space); + // And the final 2 spaces before the ascii + text->AppendL(' '); + text->AppendL(' '); + } + + + // Time to print the ascii + const TInt max = Min(numBytesOnLine, iMemBuf.Length() - idxForLine); + for (TInt j = 0; j < max; j++) + { + char ch = bptr[idxForLine + j]; + if (ch < 32 || ch >= 128) ch = '.'; + text->SetAttributesL(KNormal); + if (colorBuf[j] & EDescriptor) text->SetAttributesL(KDescriptor); + if (colorBuf[j] & ESymbol) text->SetAttributesL(KSymbol); + text->AppendL(ch); + } + text->SetAttributesL(KNormal); + text->AppendL(KCrLf); + } + + text->SetAttributesL(ConsoleAttributes::ENone); + text->Write(Stdout()); + CleanupStack::PopAndDestroy(text); + } + +/*static*/ TInt CCmdFdb::IsDescriptorHeader(TUint8 const* aHeaderPtr, TInt aMaxLen) + { + if (((TLinAddr)aHeaderPtr & 0x3) != 0) return 0; // Not aligned + TInt type = *(TUint32*)aHeaderPtr >> 28; + + if ((type == EPtr || type == EBufCPtr) && aMaxLen >= 12) return 12; // Len + maxlen + ptr + else if (type == EPtrC && aMaxLen >= 8) return 8; // Len + ptr + else if (type == EBuf || type == EBufC) + { + TInt len = (*(TInt32 const*)aHeaderPtr) & 0xfffffff; + if (len > aMaxLen || (type == 0 && len == 0)) return 0; + // Take a stab at whether it's a 16-bit descriptor + TInt wideness = 1; + TInt bufOffset = (type == EBuf ? 8 : 4); + TUint16 const* wptr = (TUint16 const*)(aHeaderPtr + bufOffset); + if (len > 4 && wptr[0] < 256 && wptr[1] < 256) wideness = 2; + return bufOffset + len * wideness; // Add 4 so the header itself is included in the calculation + } + else + { + return 0; + } + } + +void CCmdFdb::ShowBreakpointsL() + { + RBuf8 buf; + CleanupClosePushL(buf); + buf.CreateL(1024); + LeaveIfErr(iMemAccess.GetBreakpoints(buf), _L("Couldn't read breakpoint information")); + RMemoryAccess::TBreakpointInfo* bs = (RMemoryAccess::TBreakpointInfo*)buf.Ptr(); + RLtkBuf desc; + desc.CreateL(256); + TInt count = buf.Length() / sizeof(RMemoryAccess::TBreakpointInfo); + if (count) + { + for (TInt i = 0; i < count; i++) + { + RMemoryAccess::TBreakpointInfo& b = bs[i]; + Printf(_L("Breakpoint %d (thread id %u): "), b.iBreakpointId, b.iThreadId); + Write(LookupSymbol(b.iAddress)); + TBool brackets = b.iFlags & (RMemoryAccess::TBreakpointInfo::EPending | b.iFlags & RMemoryAccess::TBreakpointInfo::EHardware) || !(b.iFlags & RMemoryAccess::TBreakpointInfo::EEnabled) || b.iCondition.HasConditions(); + if (brackets) Write(_L(" (")); + desc.Zero(); + if (b.iFlags & RMemoryAccess::TBreakpointInfo::EPending) desc.Append(_L("PENDING ")); + if (!(b.iFlags & RMemoryAccess::TBreakpointInfo::EEnabled)) desc.Append(_L("DISABLED ")); + if (b.iFlags & RMemoryAccess::TBreakpointInfo::EHardware) desc.Append(_L("HARDWARE ")); + b.iCondition.Description(desc); + if (desc.Length() && desc[desc.Length()-1] == ' ') desc.SetLength(desc.Length()-1); + Write(desc); + if (brackets) Write(_L(")")); + Write(KCrLf); + } + } + else + { + Printf(_L("No breakpoints defined.\r\n")); + } + CleanupStack::PopAndDestroy(&buf); + } + +void CCmdFdb::CBreakpointNotifier::RunL() + { + if (iStatus.Int() < 0) + { + iCmd.PrintError(iStatus.Int(), _L("Error returned from NotifyBreakpoint")); + return; + } + + RMemoryAccess::TBreakpointNotification notif = iCmd.iBreakpointNotification; + iCmd.iMemAccess.NotifyBreakpoint(iCmd.iBreakpointNotificationPkg, iStatus); + SetActive(); + + iCmd.BreakpointHit(notif); + } + +void CCmdFdb::CBreakpointNotifier::DoCancel() + { + iCmd.iMemAccess.CancelNotifyBreakpoint(); + } + +CCmdFdb::CBreakpointNotifier::CBreakpointNotifier(CCmdFdb& aCmd) +: CActive(CActive::EPriorityStandard), iCmd(aCmd) + { + CActiveScheduler::Add(this); + iCmd.iMemAccess.NotifyBreakpoint(iCmd.iBreakpointNotificationPkg, iStatus); + SetActive(); + } + +CCmdFdb::CBreakpointNotifier::~CBreakpointNotifier() + { + Cancel(); + } + +void CCmdFdb::BreakpointHit(const RMemoryAccess::TBreakpointNotification& aNotif) + { + iLineEditor->RemovePromptAndUserInput(); + Printf(_L("Breakpoint %d hit in thread %u: "), aNotif.iBreakpointId, aNotif.iThreadId); + Write(LookupSymbol(aNotif.iAddress)); + Write(KCrLf); + if (iCurrent == NULL) + { + Printf(_L("(Attaching to thread %u)\r\n"), aNotif.iThreadId); + TRAP_IGNORE(AttachL(aNotif.iThreadId)); + } + iLineEditor->ReinstatePromptAndUserInput(); + } + +TInt CCmdFdb::BreakTest(TAny* /*aPtr*/) + { + //LtkUtils::Breakpoint(); + LtkUtils::RawPrint(_L8("Breaktest has completed\r\n")); + return 5; + } + +TInt CCmdFdb::BreakTestLtk(TAny* /*aPtr*/) + { + LtkUtils::Breakpoint(); + return 6; + } + +TInt CCmdFdb::BreakTestCond(TAny* /*aPtr*/) + { + CTrapCleanup* trap = CTrapCleanup::New(); + TRAPD(err, User::Leave(-1)); // This shouldn't trigger the break + TRAP(err, User::Leave(-5)); // This should + delete trap; + return 0; + } + +void CCmdFdb::ClearAllBreakpointsL() + { + RBuf8 buf; + CleanupClosePushL(buf); + buf.CreateL(1024); + LeaveIfErr(iMemAccess.GetBreakpoints(buf), _L("Couldn't read breakpoint information")); + RMemoryAccess::TBreakpointInfo* bs = (RMemoryAccess::TBreakpointInfo*)buf.Ptr(); + TInt count = buf.Length() / sizeof(RMemoryAccess::TBreakpointInfo); + for (TInt i = 0; i < count; i++) + { + RMemoryAccess::TBreakpointInfo& b = bs[i]; + TInt err = iMemAccess.ClearBreakpoint(b.iBreakpointId); + if (err) PrintWarning(_L("Couldn't clear breakpoint %d, err=%d"), b.iBreakpointId, err); + } + CleanupStack::PopAndDestroy(&buf); + } + +void CCmdFdb::CheckForConditionL(TLex& aLex, RMemoryAccess::TPredicate& aCondition) + { + aLex.SkipSpace(); + if (aLex.Eos()) return; + _LIT(KRegErr, "First argument in a conditional must be a register r0-r15"); + if (aLex.Get() != 'r') LeaveIfErr(KErrArgument, KRegErr); + TInt reg; + LeaveIfErr(aLex.Val(reg), KRegErr); + if (reg < 0 || reg > 15) LeaveIfErr(KErrArgument, KRegErr); + RMemoryAccess::TPredicate::TOp op = RMemoryAccess::TPredicate::ENothing; + TUint opchar = aLex.Get(); + switch (opchar) + { + case '<': + op = RMemoryAccess::TPredicate::ELt; + if (aLex.Peek() == '=') + { + op = RMemoryAccess::TPredicate::ELe; + aLex.Get(); + } + break; + case '>': + op = RMemoryAccess::TPredicate::EGt; + if (aLex.Peek() == '=') + { + op = RMemoryAccess::TPredicate::EGe; + aLex.Get(); + } + break; + case '!': + if (aLex.Get() != '=') LeaveIfErr(KErrArgument, _L("Unrecognised operand")); + op = RMemoryAccess::TPredicate::ENe; + break; + case '=': + op = RMemoryAccess::TPredicate::EEq; + if (aLex.Peek() == '=') aLex.Get(); // We allow == as well as = + break; + default: + LeaveIfErr(KErrArgument, _L("Unrecognised operand")); + break; + } + TInt val; + TInt err = LtkUtils::HexLex(aLex, (TUint&)val); + if (err) + { + // Try normal - this handles signed negative numbers, which HexLex doesn't afaik + err = aLex.Val(val); + } + LeaveIfErr(err, _L("Couldn't parse value")); + TBool signedCompare = ETrue; + if (aLex.Peek() == 'u' || aLex.Peek() == 'U') + { + aLex.Get(); + signedCompare = EFalse; + } + if (signedCompare) + { + op = (RMemoryAccess::TPredicate::TOp)(op + 6); // ELt -> ESignedLt etc + //Printf(_L("Op=%d reg=%d val=%d\r\n"), op, reg, val); + } + //else Printf(_L("Op=%d reg=%d val=%uu\r\n"), op, reg, (TUint)val); + if (aLex.Peek() == ',') + { + // Eat comma + aLex.Get(); + } + LeaveIfErr(aCondition.AddCondition(op, reg, (TUint32)val), _L("Couldn't add condition to TPredicate")); + }