Tidied iocli exports, build macro tweaks.
Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll.
fixed builds on platforms that don't support btrace or any form of tracing.
// 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 <fshell/ioutils.h>
#include <fshell/memoryaccesscmd.h>
#include <fshell/ltkutils.h>
#include <fshell/bsym.h>
#include <fshell/descriptorutils.h>
#include <fshell/iocons_writer.h>
#include <fshell/line_editor.h>
#include <fshell/qr3dll.h>
#include <u32std.h> // Is this available in all environments? If not, need to define TDesType ourselves
#include <babitflags.h>
#include <badesca.h>
_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<SThreadContext> 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<TCrumb> iBreadcrumbs;
RArray<TLinAddr> iMarks;
RArray<TLinAddr> iLinks;
*/
// Breakpoint support
RMemoryAccess::TBreakpointNotification iBreakpointNotification;
TPckg<RMemoryAccess::TBreakpointNotification> 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<STestData> 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<<i))
{
if (i < 10) Write(_L(" ")); // extra space for padding
Printf(_L(" R%d: "), i);
Printf(_L("%08x "), ptr[i]);
Write(LookupSymbol(ptr[i]));
Write(KCrLf);
}
}
if (valid & (1<<16))
{
Printf(_L("Flags (%cPSR): %08x\r\n"), aUserMode ? 'C' : 'S', ptr[16]); // CPSR or SPSR
}
if (valid & (1<<17))
{
// Domain Access Control Register
Printf(_L(" DACR: %08x\r\n"), ptr[16]);
}
}
void CCmdFdb::PrintRegistersLRPC(SThreadContext& aThread, TBool aUserMode)
{
// Print these in the format of the stack trace
TUint32 valid = aThread.iUserValidRegisters;
TUint32* ptr = aThread.iUserRegisters;
if (!aUserMode)
{
valid = aThread.iSupervisorValidRegisters;
ptr = aThread.iSupervisorRegisters;
}
if (valid & 1<<15)
{
Printf(_L(" R15: %08x "), ptr[15]);
Write(LookupSymbol(ptr[15]));
Write(KCrLf);
}
if (valid & 1<<14)
{
Printf(_L(" R14: %08x "), ptr[14]);
Write(LookupSymbol(ptr[14]));
Write(KCrLf);
}
}
void CCmdFdb::RefreshIfRunningL(SThreadContext& aContext)
{
if (iCurrent->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<TThreadKernelInfo> 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<RMemoryAccess::TZombieInfo>(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<CTextBuffer*>(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"));
}