Update contrib.
1 // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32utils\d_exc\minkda.cpp
15 // Example of LDD implementing a minimal kernel-side debug agent.
19 #include <kernel/kern_priv.h>
25 // Uncomment following lines to enable traces in UREL builds
27 //#define __KTRACE_OPT(c,b) b
30 // Panic category used for internal errors
31 static const char KFault[] = "MINKDA-ERROR, line:";
34 // Panic category and codes used when detecting programming error in
36 _LIT(KClientPanic, "MINKDA");
39 EPanicTrapWhileRequestPending,
40 EPanicNoCrashedThread,
41 EPanicUnsupportedRequest,
44 // As this LDD allows to bypass platform security, we need to restrict
45 // access to a few trusted clients.
46 const TUint32 KDexecSid = 0x101F7770;
48 //////////////////////////////////////////////////////////////////////////////
51 // Callback invoked on thread panic/exception and associated state.
54 class DCrashHandler : public DKernelEventHandler
57 // construction & destruction
58 inline DCrashHandler();
59 TInt Create(DLogicalDevice* aDevice);
62 void Trap(TRequestStatus* aRs, TAny* aCrashInfo);
64 void KillCrashedThread();
66 static TUint EventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis);
67 void HandleCrash(TAny* aContext);
68 void GetCpuExcInfo(const TAny* aContext, TDbgCpuExcInfo& aInfo);
70 DMutex* iHandlerMutex; // serialise access to crash handler
71 NFastSemaphore iSuspendSem; // for suspending crashed thread
72 DMutex* iDataMutex; // serialise access to following members
73 DThread* iClient; // client to signal on crash or NULL
74 TRequestStatus* iTrapRq; // signalled on crash (NULL if none)
75 TAny* iCrashInfo; // user-side buffer filled when crash trapped
76 DThread* iCrashedThread; // thread which took exception (NULL if none)
77 DLogicalDevice* iDevice; // open reference to LDD for avoiding lifetime issues
80 inline DCrashHandler::DCrashHandler()
81 : DKernelEventHandler(EventHandler, this)
86 // second-phase c'tor. Called in thread critical section.
89 TInt DCrashHandler::Create(DLogicalDevice* aDevice)
96 _LIT(KHandlerMutexName, "CtHandlerMutex");
97 r = Kern::MutexCreate(iHandlerMutex, KHandlerMutexName, KMutexOrdDebug);
100 _LIT(KDataMutexName, "CtDataMutex");
101 r = Kern::MutexCreate(iDataMutex, KDataMutexName, KMutexOrdDebug-1);
109 // Called when reference count reaches zero. At that point no threads
110 // are in the handler anymore and the handler has been removed from
114 DCrashHandler::~DCrashHandler()
116 __KTRACE_OPT(KDEBUGGER, Kern::Printf("DCrashHandler::~DCrashHandler"));
118 iDataMutex->Close(NULL);
120 iHandlerMutex->Close(NULL);
122 iDevice->Close(NULL);
125 inline TBool TookException(const DThread* aThread)
127 return aThread->iExitType == EExitPanic &&
128 aThread->iExitReason == ECausedException &&
129 aThread->iExitCategory == KLitKernExec;
133 // Called by kernel when various kinds of events occur. In thread critical
137 TUint DCrashHandler::EventHandler(TKernelEvent aEvent, TAny* a1, TAny* /*a2*/, TAny* aThis)
139 DThread* pC = &Kern::CurrentThread();
143 ((DCrashHandler*)aThis)->HandleCrash(a1);
145 case EEventKillThread:
146 if (pC->iExitType == EExitPanic && ! TookException(pC))
147 ((DCrashHandler*)aThis)->HandleCrash(NULL);
150 // ignore other events
157 // Called when an exception or panic occurs in context of thread which
158 // took the exception/panicked. In thread critical section.
161 void DCrashHandler::HandleCrash(TAny* aContext)
163 DThread* pC = &Kern::CurrentThread();
164 __KTRACE_OPT(KDEBUGGER, Kern::Printf("HandleCrash context=0x%08X thread=%O", aContext, pC));
166 // Quick exit if crashed thread is debugger (i.e. client thread
167 // which issued trap request).
170 __KTRACE_OPT(KDEBUGGER, Kern::Printf("ignoring debugger crash"));
174 // Set realtime state to off to allow us to write to possibly paged debugger thread. This is
175 // reasonable as this thread has already crashed.
176 Kern::SetRealtimeState(ERealtimeStateOff);
178 // Ensure that, at any time, at most one thread executes the following
179 // code. This simplifies user-side API.
180 Kern::MutexWait(*iHandlerMutex);
181 __ASSERT_DEBUG(iCrashedThread == NULL, Kern::Fault(KFault, __LINE__));
183 // If there is a pending trap request, store basic information
184 // about the panic/exception in user-supplied buffer and
185 // freeze the crashed thread so it can be inspected.
187 Kern::MutexWait(*iDataMutex);
191 iSuspendSem.iOwningThread = &(iCrashedThread->iNThread);
194 info.iTid = iCrashedThread->iId;
197 GetCpuExcInfo(aContext, info.iCpu);
198 info.iType = TDbgCrashInfo::EException;
201 info.iType = TDbgCrashInfo::EPanic;
202 TInt r = Kern::ThreadRawWrite(iClient, iCrashInfo, &info, sizeof(info));
203 Kern::RequestComplete(iClient, iTrapRq, r);
206 Kern::MutexSignal(*iDataMutex);
210 __KTRACE_OPT(KDEBUGGER, Kern::Printf("freezing crashed thread"));
211 NKern::FSWait(&(iSuspendSem));
212 __KTRACE_OPT(KDEBUGGER, Kern::Printf("resuming crashed thread"));
213 Kern::MutexWait(*iDataMutex);
214 // Must protect in case a cancel executes concurrently.
215 iCrashedThread = NULL;
216 Kern::MutexSignal(*iDataMutex);
219 Kern::MutexSignal(*iHandlerMutex);
223 void DCrashHandler::Trap(TRequestStatus* aRs, TAny* aCrashInfo)
226 Kern::PanicCurrentThread(KClientPanic, EPanicTrapWhileRequestPending);
227 NKern::ThreadEnterCS();
228 Kern::MutexWait(*iDataMutex);
229 iClient = &Kern::CurrentThread();
230 iCrashInfo = aCrashInfo;
232 Kern::MutexSignal(*iDataMutex);
233 NKern::ThreadLeaveCS();
237 void DCrashHandler::CancelTrap()
239 __KTRACE_OPT(KDEBUGGER, Kern::Printf(">DCrashHandler::CancelTrap"));
240 NKern::ThreadEnterCS();
241 Kern::MutexWait(*iDataMutex);
243 __KTRACE_OPT(KDEBUGGER, Kern::Printf("cancel request (0x%08X)", iTrapRq));
244 Kern::RequestComplete(iClient, iTrapRq, KErrCancel);
247 if (iCrashedThread != NULL)
249 __KTRACE_OPT(KDEBUGGER, Kern::Printf("resume crashed thread"));
250 NKern::FSSignal(&(iSuspendSem));
253 Kern::MutexSignal(*iDataMutex);
254 NKern::ThreadLeaveCS();
255 __KTRACE_OPT(KDEBUGGER, Kern::Printf("<DCrashHandler::CancelTrap"));
259 void DCrashHandler::KillCrashedThread()
261 if (iCrashedThread == NULL)
262 Kern::PanicCurrentThread(KClientPanic, EPanicNoCrashedThread);
263 NKern::FSSignal(&iSuspendSem);
267 void DCrashHandler::GetCpuExcInfo(const TAny* aContext, TDbgCpuExcInfo& aInfo)
269 #if defined(__MARM__)
270 const TArmExcInfo* pE = (const TArmExcInfo*)aContext;
271 aInfo.iFaultPc = pE->iR15;
272 aInfo.iFaultAddress = pE->iFaultAddress;
273 aInfo.iFaultStatus = pE->iFaultStatus;
274 aInfo.iExcCode = (TDbgCpuExcInfo::TExcCode)pE->iExcCode;
275 aInfo.iR13Svc = pE->iR13Svc;
276 aInfo.iR14Svc = pE->iR14Svc;
277 aInfo.iSpsrSvc = pE->iSpsrSvc;
279 (void) aContext; // silence warnings
284 //////////////////////////////////////////////////////////////////////////////
287 // Channel initialisation and cleanup. Dispatcher for user-side
288 // requests. Crash-related requests are forwarded to DCrashHandler,
289 // others are implemented here.
292 class DKdaChannel : public DLogicalChannelBase
297 // from DLogicalChannelBase
298 virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion &aVer);
299 virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
301 TInt ReadMem(RMinKda::TReadMemParams* aParams);
302 TInt GetThreadInfo(TUint aTid, TAny* aInfo);
303 void GetThreadCpuInfo(DThread* aThread, TDbgRegSet& aInfo);
304 TInt GetCodeSegs(RMinKda::TCodeSnapshotParams* aParams);
305 TInt GetCodeSegInfo(RMinKda::TCodeInfoParams* aParams);
306 TInt OpenTempObject(TUint aId, TObjectType aType);
307 void CloseTempObject();
309 DCrashHandler* iCrashHandler;
310 DObject* iTempObj; // automagically closed if abnormal termination
315 // Called when user-side thread create new channel with LDD. Called
316 // in context of that thread, in thread critical section.
319 TInt DKdaChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion &aVer)
321 if (Kern::CurrentThread().iOwningProcess->iS.iSecureId != KDexecSid)
322 return KErrPermissionDenied;
323 if (! Kern::QueryVersionSupported(KKdaLddVersion(), aVer))
324 return KErrNotSupported;
326 iCrashHandler = new DCrashHandler;
327 if (iCrashHandler == NULL)
329 return iCrashHandler->Create(iDevice);
334 // Called when last reference to channel is closed, in context of
335 // closing thread, in thread critical section.
338 DKdaChannel::~DKdaChannel()
340 __KTRACE_OPT(KDEBUGGER, Kern::Printf("DKdaChannel::~DKdaChannel"));
341 Kern::SafeClose(iTempObj, NULL);
344 iCrashHandler->CancelTrap();
345 iCrashHandler->Close();
351 // Request dispatcher. Called in context of requesting thread.
354 TInt DKdaChannel::Request(TInt aFunction, TAny* a1, TAny* a2)
356 __KTRACE_OPT(KDEBUGGER, Kern::Printf(">DKdaChannel::Request function=%d a1=0x%08X a2=0x%08X", aFunction, a1, a2));
362 iCrashHandler->Trap((TRequestStatus*)a1, a2);
364 case RMinKda::ECancelTrap:
365 iCrashHandler->CancelTrap();
367 case RMinKda::EKillCrashedThread:
368 iCrashHandler->KillCrashedThread();
370 case RMinKda::EGetThreadInfo:
371 r = GetThreadInfo((TUint)a1, a2);
373 case RMinKda::EReadMem:
374 r = ReadMem((RMinKda::TReadMemParams*)a1);
376 case RMinKda::EGetCodeSegs:
377 r = GetCodeSegs((RMinKda::TCodeSnapshotParams*)a1);
379 case RMinKda::EGetCodeSegInfo:
380 r = GetCodeSegInfo((RMinKda::TCodeInfoParams*)a1);
383 Kern::PanicCurrentThread(KClientPanic, EPanicUnsupportedRequest);
387 __KTRACE_OPT(KDEBUGGER, Kern::Printf("<DKdaChannel::Request r=%d", r));
392 TInt DKdaChannel::ReadMem(RMinKda::TReadMemParams* aParams)
394 RMinKda::TReadMemParams params;
395 umemget32(¶ms, aParams, sizeof(params));
399 TUint8* destPtr = (TUint8*)Kern::KUDesInfo(*params.iDes, destLen, destMax);
401 TInt r = OpenTempObject(params.iTid, EThread);
404 r = Kern::ThreadRawRead((DThread*)iTempObj, (TAny*)params.iAddr, destPtr, destMax);
407 Kern::KUDesSetLength(*params.iDes, destMax);
416 TInt DKdaChannel::GetThreadInfo(TUint aTid, TAny* aInfo)
418 TInt r = OpenTempObject(aTid, EThread);
421 DThread* pT = (DThread*)iTempObj;
423 pT->FullName(info.iFullName);
424 info.iPid = pT->iOwningProcess->iId;
425 info.iStackBase = pT->iUserStackRunAddress;
426 info.iStackSize = pT->iUserStackSize;
427 info.iExitCategory = pT->iExitCategory;
428 info.iExitReason = pT->iExitReason;
429 GetThreadCpuInfo(pT, info.iCpu);
430 umemput32(aInfo, &info, sizeof(info));
436 // :FIXME: improve API
437 TInt DKdaChannel::GetCodeSegs(RMinKda::TCodeSnapshotParams* aParams)
439 RMinKda::TCodeSnapshotParams params;
440 umemget32(¶ms, aParams, sizeof(params));
443 umemget32(&maxcount, params.iCountPtr, sizeof(maxcount));
445 __KTRACE_OPT(KDEBUGGER, Kern::Printf(">DKdaChannel::GetCodeSegs pid=%d maxcount=%d", params.iPid, maxcount));
447 __ASSERT_DEBUG(! iTempObj, Kern::Fault(KFault, __LINE__));
448 TInt r = OpenTempObject(params.iPid, EProcess);
451 __KTRACE_OPT(KDEBUGGER, Kern::Printf("<DKdaChannel::GetCodeSegs process not found"));
455 DProcess* pP = (DProcess*)iTempObj;
460 TInt actcount = pP->TraverseCodeSegs(&q, NULL, DCodeSeg::EMarkDebug, DProcess::ETraverseFlagAdd);
464 TInt n = Min(actcount, maxcount);
465 SDblQueLink* pL = q.iA.iNext;
467 for (TInt i=0; i<n; ++i, pL = pL->iNext)
469 DCodeSeg* pS = _LOFF(pL, DCodeSeg, iTempLink);
470 XTRAP(r, XT_DEFAULT, umemput32(params.iHandles + i, &pS, sizeof(TAny*)));
475 DCodeSeg::EmptyQueue(q, DCodeSeg::EMarkDebug);
477 Kern::EndAccessCode();
479 if (r == KErrBadDescriptor)
480 Kern::PanicCurrentThread(KLitKernExec, ECausedException);
481 umemput32(params.iCountPtr, &actcount, sizeof(actcount));
483 __KTRACE_OPT(KDEBUGGER, Kern::Printf("<DKdaChannel::GetCodeSegs actcount=%d", actcount));
488 // :FIXME: improve API
489 TInt DKdaChannel::GetCodeSegInfo(RMinKda::TCodeInfoParams* aParams)
491 RMinKda::TCodeInfoParams params;
492 umemget32(¶ms, aParams, sizeof(params));
494 // :FIXME: Currently code segments are always loaded at the same
495 // location in every address space. Consequently we can ignore
496 // the PID provided by the client.
499 TInt r = KErrNotFound;
500 TFileName nameBuffer;
503 DCodeSeg* pS = DCodeSeg::VerifyHandle(params.iHandle);
506 TModuleMemoryInfo mmi;
507 r = pS->GetMemoryInfo(mmi, pP);
510 params.iCodeBase = mmi.iCodeBase;
511 params.iCodeSize = mmi.iCodeSize;
512 XTRAP(r, XT_DEFAULT, nameBuffer.Append(*(pS->iFileName)));
515 Kern::EndAccessCode();
516 Kern::KUDesPut(*(params.iPathPtr), nameBuffer);
517 if (r == KErrBadDescriptor)
518 Kern::PanicCurrentThread(KLitKernExec, ECausedException);
521 umemput32(aParams, ¶ms, sizeof(params));
527 // Lookup a thread or process id and open the corresponding object.
529 // The object is stored in DKdaChannel::iTempObj to ensure it will be
530 // closed even if the client thread terminates unexpectedly. The
531 // caller must call CloseTempObject() when it is finished with it.
534 TInt DKdaChannel::OpenTempObject(TUint aId, TObjectType aType)
536 __ASSERT_DEBUG(aType == EProcess || aType == EThread, Kern::Fault(KFault, __LINE__));
537 __ASSERT_DEBUG(! iTempObj, Kern::Fault(KFault, __LINE__));
539 DObjectCon* pC = Kern::Containers()[aType];
540 NKern::ThreadEnterCS();
542 DObject* tempObj = (aType == EProcess) ? (DObject*)Kern::ProcessFromId(aId) : (DObject*)Kern::ThreadFromId(aId);
545 TInt r = KErrNotFound;
547 r = iTempObj->Open();
549 NKern::UnlockSystem();
551 NKern::ThreadLeaveCS();
555 void DKdaChannel::CloseTempObject()
557 __ASSERT_DEBUG(iTempObj, Kern::Fault(KFault, __LINE__));
558 NKern::ThreadEnterCS();
559 iTempObj->Close(NULL);
561 NKern::ThreadLeaveCS();
566 void DKdaChannel::GetThreadCpuInfo(DThread* aThread, TDbgRegSet& aInfo)
568 __ASSERT_DEBUG(aThread != &Kern::CurrentThread(), Kern::Fault(KFault, __LINE__));
572 NKern::ThreadGetUserContext(&(aThread->iNThread), ®Set, unused);
573 aInfo.iRn[0] = regSet.iR0;
574 aInfo.iRn[1] = regSet.iR1;
575 aInfo.iRn[2] = regSet.iR2;
576 aInfo.iRn[3] = regSet.iR3;
577 aInfo.iRn[4] = regSet.iR4;
578 aInfo.iRn[5] = regSet.iR5;
579 aInfo.iRn[6] = regSet.iR6;
580 aInfo.iRn[7] = regSet.iR7;
581 aInfo.iRn[8] = regSet.iR8;
582 aInfo.iRn[9] = regSet.iR9;
583 aInfo.iRn[10] = regSet.iR10;
584 aInfo.iRn[11] = regSet.iR11;
585 aInfo.iRn[12] = regSet.iR12;
586 aInfo.iRn[13] = regSet.iR13;
587 aInfo.iRn[14] = regSet.iR14;
588 aInfo.iRn[15] = regSet.iR15;
589 aInfo.iCpsr = regSet.iFlags;
594 void DKdaChannel::GetThreadCpuInfo(DThread* /*aThread*/, TDbgRegSet& /*aInfo*/)
601 //////////////////////////////////////////////////////////////////////////////
603 class DCtDevice : public DLogicalDevice
607 // from DLogicalDevice
608 virtual TInt Install();
609 virtual void GetCaps(TDes8& aDes) const;
610 virtual TInt Create(DLogicalChannelBase*& aChannel);
613 DCtDevice::DCtDevice()
617 iVersion = KKdaLddVersion();
620 TInt DCtDevice::Install()
622 return SetName(&KKdaLddName);
625 void DCtDevice::GetCaps(TDes8& /*aDes*/) const
629 TInt DCtDevice::Create(DLogicalChannelBase*& aChannel)
631 aChannel = new DKdaChannel;
632 return (aChannel != NULL) ? KErrNone : KErrNoMemory;
635 //////////////////////////////////////////////////////////////////////////////
637 DECLARE_STANDARD_LDD()
639 return new DCtDevice;