First submission to Symbian Foundation staging server.
// 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"));
}