sl@0: // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32\debug\crashMonitor\src\scmdatasave.cpp sl@0: // sl@0: // sl@0: sl@0: #define __INCLUDE_REG_OFFSETS__ // for SP_R13U in nk_plat.h sl@0: sl@0: #include sl@0: #include "arm_mem.h" sl@0: #include "nk_plat.h" sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: /** sl@0: * @file sl@0: * @internal technology sl@0: */ sl@0: sl@0: /** sl@0: * SCMDataSave constructor sl@0: * @param aMonitor - the monitor which has caught the syetem crash this object is saving data for sl@0: * @param aFlash - the flash memory data will be written to, note the CrashFlash interface is sl@0: * rather limited and does not support partial block writes sl@0: * @param aFlashInfo - data describing the structure of the flash data sl@0: */ sl@0: EXPORT_C SCMDataSave::SCMDataSave(Monitor* aMonitor, CrashFlash* aFlash) sl@0: : iMonitor(aMonitor) sl@0: ,iFlash(aFlash) sl@0: ,iByteCount(0) sl@0: #ifdef SCM_COMM_OUTPUT sl@0: ,iWriteSelect(EWriteComm) // write data to debug port sl@0: #else sl@0: ,iWriteSelect(EWriteFlash) // write data to flash sl@0: #endif sl@0: ,iPerformChecksum(ETrue) // checksum data sl@0: ,iStartingPointForCrash(0) sl@0: { sl@0: const TInt KCacheSize = 128; sl@0: iFlashCache = HBuf8::New(KCacheSize); sl@0: CLTRACE1("(SCMDataSave) Creating writer with cache size = %d", KCacheSize); sl@0: iWriter = new TCachedByteStreamWriter(const_cast(iFlashCache->Ptr()), KCacheSize); sl@0: iWriter->SetWriterImpl(this); sl@0: } sl@0: sl@0: /** sl@0: * Destructor sl@0: */ sl@0: SCMDataSave::~SCMDataSave() sl@0: { sl@0: delete iFlashCache; sl@0: } sl@0: sl@0: /** sl@0: * Getter for the current byte count. This is the amount of data that has currently sl@0: * been written to given media for this crash log sl@0: * @return The number of bytes written already to given media sl@0: */ sl@0: TInt SCMDataSave::GetByteCount() sl@0: { sl@0: return iByteCount; sl@0: } sl@0: sl@0: /** sl@0: * Logs the user stack for a given DThread object if it is available sl@0: * @param aThread - thread whose stack we wish to log sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the OS codes sl@0: */ sl@0: TInt SCMDataSave::LogThreadUserStack(DThread* aThread, TBool aFullStack, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: TUint memDumped = 0; sl@0: sl@0: TUint svSp, usrSp; sl@0: iMonitor->GetStackPointers(&(aThread->iNThread), svSp, usrSp ); sl@0: sl@0: //first we check for a user stack... sl@0: if (aThread->iUserStackRunAddress && aThread->iUserStackSize) sl@0: { sl@0: //Get data together sl@0: TThreadStack usrStack; sl@0: usrStack.iStackType = TThreadStack::EUsrStack; sl@0: usrStack.iThreadId = (TUint64)aThread->iId; sl@0: sl@0: //map in the user stack sl@0: TUint8* usrStart = (TUint8*)iMonitor->MapAndLocateUserStack(aThread); //What about Demand paging?? sl@0: TUint8* usrEnd = (TUint8*)(usrStart + aThread->iUserStackSize); sl@0: if(usrStart) sl@0: { sl@0: TUint8* stackPointer = (TUint8*)usrSp; sl@0: sl@0: //check the stack pointer is in the range of the stack... sl@0: if (stackPointer < usrStart || stackPointer >= usrEnd) sl@0: { sl@0: stackPointer = usrStart; sl@0: } sl@0: sl@0: //log the size of the stack we are dumping sl@0: usrStack.iStackSize = aFullStack || (stackPointer == usrStart) ? usrEnd - usrStart : usrEnd - stackPointer; sl@0: TUint8* dumpFrom = aFullStack ? usrStart : stackPointer; sl@0: sl@0: //write the stack sl@0: aSizeDumped+= usrStack.GetSize(); sl@0: usrStack.Serialize(*iWriter); sl@0: sl@0: //now we dump the actual stack sl@0: //if there is a memErr when we read, there isnt much we can do - possibly a bit in the struct to say available/not available? sl@0: //-1 because we dont want to write the byte at usrEnd sl@0: MTRAPD(memErr, LogMemory(dumpFrom, usrStack.iStackSize, aThread, memDumped)); sl@0: if(KErrNone != memErr) sl@0: { sl@0: CLTRACE("Failed to log usr stack"); sl@0: } sl@0: sl@0: aSizeDumped+= memDumped; sl@0: } sl@0: else sl@0: { sl@0: //write the struct sl@0: aSizeDumped+=usrStack.GetSize(); sl@0: usrStack.Serialize(*iWriter); sl@0: } sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Logs the supervisor stack for a given DThread object sl@0: * @param aThread - thread whose stack we wish to log sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the system wide codes sl@0: */ sl@0: TInt SCMDataSave::LogThreadSupervisorStack(DThread* aThread, TBool aFullStack, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: TUint memDumped; sl@0: sl@0: TUint svSp, usrSp; sl@0: iMonitor->GetStackPointers(&(aThread->iNThread), svSp, usrSp ); sl@0: sl@0: //now we dump the supervisor stack sl@0: TThreadStack svrStack; sl@0: svrStack.iStackType = TThreadStack::ESvrStack; sl@0: svrStack.iThreadId = (TUint64)aThread->iId; sl@0: sl@0: if (aThread->iSupervisorStack && aThread->iSupervisorStackSize) sl@0: { sl@0: TUint8* svrStart = (TUint8*)aThread->iSupervisorStack; sl@0: TUint8* svrEnd = (TUint8*)(svrStart + aThread->iSupervisorStackSize); sl@0: TUint8* svrStackPointer = (TUint8*)svSp; sl@0: sl@0: //size of stack we are to dump sl@0: svrStack.iStackSize = aFullStack || (svrStackPointer == svrStart) ? svrEnd - svrStart : svrEnd - svrStackPointer; sl@0: sl@0: if(svrStart) sl@0: { sl@0: //check the stack pointer is in the range of the stack... sl@0: if (svrStackPointer < svrStart || svrStackPointer >= svrEnd) sl@0: { sl@0: svrStackPointer = svrStart; sl@0: } sl@0: sl@0: //write struct to flash sl@0: aSizeDumped+=svrStack.GetSize(); sl@0: svrStack.Serialize(*iWriter); sl@0: sl@0: //now we dump the actual stack sl@0: //if there is a memErr when we read, there isnt much we can do - possibly a bit in the struct to say available/not available? sl@0: MTRAPD(memErr, LogMemory(svrStart, svrStack.iStackSize, aThread, memDumped)); sl@0: aSizeDumped+=memDumped; sl@0: sl@0: if(KErrNone != memErr) sl@0: { sl@0: CLTRACE("Failed to log supervisor stack"); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: //write the struct sl@0: aSizeDumped+=svrStack.GetSize(); sl@0: svrStack.Serialize(*iWriter); sl@0: } sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Takes a DProcess kernel object and logs its corrosponding code segments sl@0: * @param aProcess sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the OS wide error codes sl@0: */ sl@0: TInt SCMDataSave::LogCodeSegments(DProcess* aProc, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: sl@0: //the code segment set for this process sl@0: TCodeSegmentSet segSet; sl@0: segSet.iPid = (TUint64)aProc->iId; sl@0: sl@0: //make sure list mutex is ok sl@0: if(Kern::CodeSegLock()->iHoldCount) sl@0: { sl@0: return KErrCorrupt; sl@0: } sl@0: sl@0: //get code seg list sl@0: SDblQue queue; sl@0: aProc->TraverseCodeSegs(&queue, NULL, DCodeSeg::EMarkDebug, DProcess::ETraverseFlagAdd); sl@0: sl@0: //iterate through the list sl@0: TInt codeSegCnt = 0; sl@0: for(SDblQueLink* codeSegPtr= queue.First(); codeSegPtr!=(SDblQueLink*) (&queue); codeSegPtr=codeSegPtr->iNext) sl@0: { sl@0: //get the code seg sl@0: DEpocCodeSeg* codeSeg = (DEpocCodeSeg*)_LOFF(codeSegPtr,DCodeSeg, iTempLink); sl@0: sl@0: if(codeSeg) sl@0: { sl@0: codeSegCnt++; sl@0: } sl@0: } sl@0: sl@0: if(codeSegCnt == 0) sl@0: { sl@0: return KErrNone; sl@0: } sl@0: sl@0: segSet.iNumSegs = codeSegCnt; sl@0: segSet.Serialize(*iWriter); sl@0: aSizeDumped+=segSet.GetSize(); sl@0: sl@0: TModuleMemoryInfo memoryInfo; sl@0: sl@0: //now we write each code segment sl@0: for(SDblQueLink* codeSegPtr= queue.First(); codeSegPtr!=(SDblQueLink*) (&queue); codeSegPtr=codeSegPtr->iNext) sl@0: { sl@0: //get the code seg sl@0: DEpocCodeSeg* codeSeg = (DEpocCodeSeg*)_LOFF(codeSegPtr,DCodeSeg, iTempLink); sl@0: sl@0: if(codeSeg) sl@0: { sl@0: TCodeSegment seg; sl@0: seg.iXip = (codeSeg->iXIP) ? ETrue : EFalse; sl@0: sl@0: //Get the code seg type sl@0: if(codeSeg->IsExe()) sl@0: { sl@0: seg.iCodeSegType = EExeCodeSegType; sl@0: } sl@0: else if(codeSeg->IsDll()) sl@0: { sl@0: seg.iCodeSegType = EDllCodeSegType; sl@0: } sl@0: sl@0: TInt err = codeSeg->GetMemoryInfo(memoryInfo, NULL); sl@0: if(KErrNone == err) sl@0: { sl@0: seg.iCodeSegMemInfo = memoryInfo; sl@0: } sl@0: else sl@0: { sl@0: seg.iCodeSegMemInfo.iCodeSize = 0; sl@0: sl@0: // Still need to indicate it wasnt available somehow sl@0: } sl@0: sl@0: //Get filename sl@0: seg.iNameLength = codeSeg->iFileName->Length(); sl@0: seg.iName = *(codeSeg->iFileName); sl@0: sl@0: aSizeDumped+=seg.GetSize(); sl@0: seg.Serialize(*iWriter); sl@0: } sl@0: } sl@0: sl@0: //Empty this queue and clear marks sl@0: DCodeSeg::EmptyQueue(queue, DCodeSeg::EMarkDebug); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * This logs the rom version and header information to the crash media sl@0: * @param aSizeDumped amount of data occupied sl@0: * @return one of the OS wide codes sl@0: */ sl@0: TInt SCMDataSave::LogRomInfo(TUint& aSizeDumped) sl@0: { sl@0: aSizeDumped = 0; sl@0: sl@0: TRomHeaderData romData; sl@0: sl@0: TRomHeader rHdr = Epoc::RomHeader(); sl@0: sl@0: romData.iMajorVersion = rHdr.iVersion.iMajor; sl@0: romData.iMinorVersion = rHdr.iVersion.iMinor; sl@0: romData.iBuildNumber = rHdr.iVersion.iBuild; sl@0: romData.iTime = rHdr.iTime; sl@0: sl@0: TInt err = romData.Serialize(*iWriter); sl@0: if(KErrNone != err) sl@0: { sl@0: return err; sl@0: } sl@0: sl@0: aSizeDumped += romData.GetSize(); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Takes a DProcess kernel object and logs to flash sl@0: * @param aProc sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the OS wide error codes sl@0: */ sl@0: TInt SCMDataSave::LogProcessData(DProcess* aProc, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: sl@0: TProcessData procData; sl@0: DCodeSeg* codeSeg = aProc->iCodeSeg; sl@0: sl@0: procData.iPriority = aProc->iPriority; sl@0: procData.iPid = (TUint64)aProc->iId; sl@0: sl@0: //the code segment is not always available sl@0: if(codeSeg) sl@0: { sl@0: procData.iNamesize = codeSeg->iFileName->Length(); sl@0: procData.iName = *(codeSeg->iFileName); sl@0: } sl@0: sl@0: aSizeDumped += procData.GetSize(); sl@0: procData.Serialize(*iWriter); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Creates meta data about the crash such as time of crash, exit reason etc. to be logged sl@0: * later on when we have log size. sl@0: * @param aCategory - crash category sl@0: * @param aReason - crash reason sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the OS wide codes sl@0: */ sl@0: TInt SCMDataSave::LogCrashHeader(const TDesC8& aCategory, TInt aReason, TInt aCrashId, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: sl@0: //the thread that crashed is the context in which we are running sl@0: DThread* crashedThread = &Kern::CurrentThread(); sl@0: sl@0: iCrashInf.iPid = crashedThread->iOwningProcess->iId; sl@0: iCrashInf.iTid = crashedThread->iId; sl@0: iCrashInf.iCrashTime = CrashTime(); sl@0: iCrashInf.iExitType = 0; // Not yet done: Exception or Fault - should be in category sl@0: iCrashInf.iExitReason = aReason; sl@0: iCrashInf.iFlashAlign = KFlashAlignment; //record the flash alignment (word aligned for now) sl@0: iCrashInf.iCachedWriterSize = iWriter->GetCacheSize(); sl@0: sl@0: iCrashInf.iCategorySize = aCategory.Length(); sl@0: iCrashInf.iCategory = aCategory; sl@0: iCrashInf.iCrashId = aCrashId; sl@0: sl@0: iCrashInf.iFlashBlockSize = KCrashLogBlockSize;; sl@0: iCrashInf.iFlashPartitionSize = KCrashLogSize;; sl@0: sl@0: TSuperPage& sp=Kern::SuperPage(); sl@0: iCrashInf.iExcCode = sp.iKernelExcId; sl@0: sl@0: //These will be updated with more info at end of crash sl@0: aSizeDumped+=iCrashInf.GetSize(); sl@0: iCrashInf.Serialize(*iWriter); sl@0: sl@0: aSizeDumped+=iHdr.GetSize(); sl@0: iHdr.Serialize(*iWriter); sl@0: sl@0: CLTRACE1("(SCMDataSave::LogCrashHeader) finished bytes written= %d", iWriter->GetBytesWritten()); sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Logs meta data about a given DThread object sl@0: * @param aThread Thread to dump sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return sl@0: */ sl@0: TInt SCMDataSave::LogThreadData(DThread* aThread, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: sl@0: //struct to hold data that gets written to flash sl@0: TThreadData threadData; sl@0: sl@0: threadData.iTid = (TUint64)aThread->iId; sl@0: threadData.iOwnerId = (TUint64)aThread->iOwningProcess->iId; sl@0: threadData.iPriority = aThread->iThreadPriority; sl@0: sl@0: //Get the stack pointers sl@0: TUint svSp, usrSp; sl@0: iMonitor->GetStackPointers(&(aThread->iNThread), svSp, usrSp ); sl@0: threadData.iUsrSP = usrSp; sl@0: threadData.iSvcSP = svSp; sl@0: sl@0: //supervisor and user stack details sl@0: threadData.iSvcStack = (TInt32)aThread->iSupervisorStack; sl@0: threadData.iSvcStacksize = aThread->iSupervisorStackSize; sl@0: threadData.iUsrStack = aThread->iUserStackRunAddress; sl@0: threadData.iUsrStacksize = aThread->iUserStackSize; sl@0: sl@0: //currently we can only get the kernels heap sl@0: if(aThread == &Kern::CurrentThread()) sl@0: { sl@0: TInt32 heapLoc = 0; sl@0: TInt32 heapSz = 0; sl@0: TInt err = FindKernelHeap(heapLoc,heapSz); sl@0: if(KErrNone == err) sl@0: { sl@0: threadData.iSvcHeap = heapLoc; sl@0: threadData.iSvcHeapSize = heapSz; sl@0: } sl@0: else sl@0: { sl@0: CLTRACE("\tError: Unable to get kernel heap"); sl@0: } sl@0: } sl@0: sl@0: //get filename sl@0: TFileName filename; sl@0: aThread->TraceAppendFullName(filename, EFalse); sl@0: sl@0: threadData.iName.Copy(filename); sl@0: threadData.iNamesize = threadData.iName.Length(); sl@0: sl@0: sl@0: #ifdef __INCLUDE_NTHREADBASE_DEFINES__ sl@0: threadData.iLastCpu = aThread->iNThread.iLastCpu; sl@0: #else sl@0: threadData.iLastCpu = aThread->iNThread.iSpare3; sl@0: #endif sl@0: sl@0: threadData.Serialize(*iWriter); sl@0: aSizeDumped+=threadData.GetSize(); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Logs the arm exception stacks sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the OS wide codes sl@0: */ sl@0: TInt SCMDataSave::LogExceptionStacks(TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: TUint memDumped = 0; sl@0: sl@0: #if defined(__EPOC32__) && !defined(__CPU_X86) sl@0: sl@0: TStackInfo& stackInfo = Kern::SuperPage().iStackInfo; sl@0: sl@0: TThreadStack irqStack; sl@0: irqStack.iStackType = TThreadStack::EIRQStack; sl@0: irqStack.iStackSize = stackInfo.iIrqStackSize; sl@0: sl@0: aSizeDumped+=irqStack.GetSize(); sl@0: irqStack.Serialize(*iWriter); sl@0: sl@0: //now dump the IRQ memory - not much we can do in the event of an error sl@0: MTRAPD(irqErr, LogMemory((TUint8*)stackInfo.iIrqStackBase, stackInfo.iIrqStackSize, &Kern::CurrentThread(), memDumped)); sl@0: sl@0: if(KErrNone != irqErr) sl@0: { sl@0: CLTRACE("*****Failed to log IRQ stack"); sl@0: } sl@0: aSizeDumped+=memDumped; sl@0: sl@0: //Next, we do the FIQ stack sl@0: TThreadStack fiqStack; sl@0: fiqStack.iStackType = TThreadStack::EFIQStack; sl@0: fiqStack.iStackSize = stackInfo.iFiqStackSize; sl@0: sl@0: aSizeDumped+=fiqStack.GetSize(); sl@0: fiqStack.Serialize(*iWriter); sl@0: sl@0: //Now dump the stack itself sl@0: MTRAPD(fiqErr, LogMemory((TUint8*)stackInfo.iFiqStackBase, stackInfo.iFiqStackSize, &Kern::CurrentThread(), memDumped)); sl@0: sl@0: if(KErrNone != fiqErr ) sl@0: { sl@0: CLTRACE("*****Failed to log FIQ stack"); sl@0: } sl@0: aSizeDumped+=memDumped; sl@0: sl@0: #endif sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Logs the CPU Registers at the time of crash sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return system wide OS code sl@0: */ sl@0: TInt SCMDataSave::LogCPURegisters(TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: sl@0: TInt32 fullSet = 37; sl@0: sl@0: //meta data about the thread set sl@0: TRegisterSet threadSet; sl@0: threadSet.iNumRegisters = fullSet; sl@0: sl@0: aSizeDumped+=threadSet.GetSize(); sl@0: threadSet.Serialize(*iWriter); sl@0: sl@0: SFullArmRegSet regSet; sl@0: ReadCPURegisters(regSet); sl@0: TArmReg* regs = (TArmReg*)®Set; sl@0: sl@0: TInt32 cnt = 0; sl@0: for(cnt = 0; cnt < fullSet; cnt++) sl@0: { sl@0: //this is the struct to store the register value in sl@0: TRegisterValue regVal; sl@0: regVal.iType = cnt * 0x100; sl@0: regVal.iValue32 = regs[cnt]; sl@0: regVal.iOwnId = Kern::CurrentThread().iId; sl@0: sl@0: aSizeDumped+=regVal.GetSize(); sl@0: regVal.Serialize(*iWriter); sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * This logs the registers for a given thread to the flash memory sl@0: * @param aThread - thread whose registers we want sl@0: * @param aRegType - type of register set required such as user, supervisor etc sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the OS return codes sl@0: */ sl@0: TInt SCMDataSave::LogRegisters(DThread* aThread, const TRegisterSetType& aRegType, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: sl@0: TArmRegSet regs; sl@0: TUint32 availableRegs; sl@0: TInt err; sl@0: sl@0: //for the current thread we do things differently sl@0: if(aThread == &Kern::CurrentThread() && aRegType == EFullCPURegisters) sl@0: { sl@0: err = LogCPURegisters(aSizeDumped); sl@0: return err; sl@0: } sl@0: else if(aThread == &Kern::CurrentThread()) sl@0: { sl@0: //only do full cpu reg for the current thread sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: //Read the appropriate registers sl@0: switch(aRegType) sl@0: { sl@0: case EUserRegisters : sl@0: { sl@0: err = ReadUserRegisters(aThread, regs, availableRegs); sl@0: break; sl@0: } sl@0: case ESupervisorRegisters : sl@0: { sl@0: err = ReadSystemRegisters(aThread, regs, availableRegs); sl@0: break; sl@0: } sl@0: default : return KErrNotSupported; sl@0: } sl@0: sl@0: if(err != KErrNone) sl@0: { sl@0: return err; sl@0: } sl@0: sl@0: //meta data about the thread set sl@0: TRegisterSet threadSet; sl@0: sl@0: //to get the number of registers in advance, we need to count the number of times 1 is set in the bit field of availableRegs sl@0: TUint numR = 0; sl@0: for(TInt cnt =0; cnt< 8*sizeof(availableRegs); cnt++) //cycle through 1 bit at a time sl@0: { sl@0: if(0x1 & (availableRegs>>cnt)) sl@0: numR++; sl@0: } sl@0: sl@0: threadSet.iNumRegisters = numR; sl@0: sl@0: if(numR == 0) sl@0: return KErrNone; sl@0: sl@0: threadSet.Serialize(*iWriter); sl@0: aSizeDumped += threadSet.GetSize(); sl@0: sl@0: TInt32 currentRegister = 1; sl@0: TArmReg* reg = (TArmReg*)(®s); sl@0: sl@0: for(TInt32 cnt = 0; cnt < KArmRegisterCount; cnt++) sl@0: { sl@0: //look at the unavailable bitmask to see current register is available sl@0: //only write the registers we have values for sl@0: if(currentRegister & availableRegs) sl@0: { sl@0: //this is the struct to store the register value in sl@0: TRegisterValue regVal; sl@0: sl@0: //get register type as per symbian elf docs sl@0: TUint32 registerType; sl@0: err = GetRegisterType(aRegType, cnt, registerType); sl@0: if(err != KErrNone) sl@0: { sl@0: continue; sl@0: } sl@0: regVal.iType = registerType; sl@0: regVal.iOwnId = aThread->iId; sl@0: sl@0: //set value sl@0: regVal.iValue32 = reg[cnt]; sl@0: sl@0: aSizeDumped+=regVal.GetSize(); sl@0: regVal.Serialize(*iWriter); sl@0: } sl@0: sl@0: currentRegister<<=1; sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * This logs memory in the specified area sl@0: * @param aStartAddress - address to start from sl@0: * @param aEndAddress - address to finish sl@0: * @param aThread - process whose memory this is in sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the system wide codes sl@0: */ sl@0: TInt SCMDataSave::LogMemory(const TUint8* aStartAddress, TInt aLength, const DThread* aThread, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: sl@0: if(aThread->iOwningProcess != &Kern::CurrentProcess()) sl@0: { sl@0: TInt err = iMonitor->SwitchAddressSpace(aThread->iOwningProcess, ETrue); sl@0: if(KErrNone != err) sl@0: { sl@0: return err; sl@0: } sl@0: } sl@0: sl@0: TMemoryDump memDump; sl@0: memDump.iStartAddress = (TUint32)aStartAddress; sl@0: memDump.iLength = aLength; sl@0: memDump.iPid = aThread->iOwningProcess->iId; sl@0: sl@0: aSizeDumped+=memDump.GetSize(); sl@0: memDump.Serialize(*iWriter); sl@0: sl@0: if(!aStartAddress) sl@0: { sl@0: return KErrArgument; sl@0: } sl@0: sl@0: TRawData theMemory; sl@0: theMemory.iData.Set(const_cast(aStartAddress), aLength, aLength); sl@0: sl@0: theMemory.Serialize(*iWriter); sl@0: aSizeDumped+=theMemory.GetSize(); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * This logs the locks held by system at time of crash sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return one of the system wide codes sl@0: */ sl@0: TInt SCMDataSave::LogLocks(TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: sl@0: // get the mutex logs & waits & log via a TLockData object sl@0: TSCMLockData lockData; sl@0: sl@0: const TInt KMaxLockCheck = 20; // so no possibility of infinite loop sl@0: sl@0: TInt lockCount = 0; sl@0: // check for kernel locks - sl@0: for(TInt i=0;iiHoldCount); sl@0: lockData.SetMutexThreadWaitCount(mutex->iWaitCount); sl@0: } sl@0: else sl@0: { sl@0: // no mutex held set to -1 sl@0: lockData.SetMutexHoldCount(0); sl@0: lockData.SetMutexThreadWaitCount(0); sl@0: } sl@0: sl@0: aSizeDumped+=lockData.GetSize(); sl@0: TInt err = lockData.Serialize(*iWriter); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: * Writes the SCM Configuration to the start of the media sl@0: * @param aScmConfig Configuration to write sl@0: * @return one of the system wide codes sl@0: */ sl@0: TInt SCMDataSave::LogConfig(SCMConfiguration& aScmConfig) sl@0: { sl@0: iWriter->SetPosition(0); sl@0: sl@0: TInt err = aScmConfig.Serialize(*iWriter); sl@0: sl@0: if( err != KErrNone) sl@0: { sl@0: CLTRACE1("SCMDataSave::LogConfig failed err = %d", err); sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: * Reads the SCM Configuration from the media sl@0: * @param aScmConfig sl@0: * @return one of the system wide codes sl@0: */ sl@0: TInt SCMDataSave::ReadConfig(SCMConfiguration& aScmConfig) sl@0: { sl@0: const TInt KBufSize = 135; //Not yet done: Put in header, beside config defn sl@0: sl@0: if( KBufSize < aScmConfig.GetSize()) sl@0: { sl@0: CLTRACE2("(SCMDataSave::ReadConfig) ** ERROR Inadequate buffer actual = %d req = %d" sl@0: , KBufSize, aScmConfig.GetSize()); sl@0: } sl@0: sl@0: // try and read the configuration sl@0: TBuf8 buf; sl@0: buf.SetLength(KBufSize); sl@0: sl@0: iFlash->SetReadPos(0); // config always at 0 sl@0: iFlash->Read(buf); sl@0: sl@0: TByteStreamReader reader(const_cast(buf.Ptr())); sl@0: TInt err = aScmConfig.Deserialize(reader); sl@0: if(err == KErrNotReady) sl@0: { sl@0: CLTRACE("(SCMDataSave::ReadConfig) no config saved - use default"); sl@0: } sl@0: else if(err == KErrNone) sl@0: { sl@0: CLTRACE("(SCMDataSave::ReadConfig) Config read ok"); sl@0: } sl@0: else sl@0: { sl@0: CLTRACE1("(SCMDataSave::ReadConfig) error reading config err = %d", err); sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: * This is a look up table to map the register type and number to the symbian elf definition sl@0: * of register type sl@0: * @param aSetType this is the register set type - user, supervisor etc sl@0: * @param aRegNumber this is the number of the register as per TArmRegisters in arm_types.h sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return One of the OS wide codes sl@0: */ sl@0: TInt SCMDataSave::GetRegisterType(const TRegisterSetType& aSetType, TInt32& aRegNumber, TUint32& aRegisterType) sl@0: { sl@0: //validate arguments sl@0: if(aRegNumber < EArmR0 || aRegNumber > EArmFlags) sl@0: { sl@0: return KErrArgument; sl@0: } sl@0: sl@0: //look at what type we are using sl@0: switch(aSetType) sl@0: { sl@0: case EUserRegisters : sl@0: { sl@0: aRegisterType = aRegNumber * 0x100; //for R0 to R16 (CPSR) it just increments in 0x100 from 0x0 to 0x1000 sl@0: break; sl@0: } sl@0: case ESupervisorRegisters : sl@0: { sl@0: //same as EUserRegisters except R13 and R14 are different sl@0: if(aRegNumber == EArmSp) sl@0: { sl@0: aRegisterType = 0x1100; sl@0: break; sl@0: } sl@0: else if(aRegNumber == EArmLr) sl@0: { sl@0: aRegisterType = 0x1200; sl@0: break; sl@0: } sl@0: else sl@0: { sl@0: aRegisterType = aRegNumber * 0x100; sl@0: break; sl@0: } sl@0: } sl@0: default : return KErrNotSupported; sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Writes the trace buffer to the crash log. sl@0: * @param aSizeToDump Number of bytes to dump. If this is zero we attempt to write the entire buffer sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return One of the OS wide codes sl@0: */ sl@0: TInt SCMDataSave::LogTraceBuffer(TInt aSizeToDump, TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: aSizeDumped = 0; sl@0: TUint memDumped = 0; sl@0: sl@0: TBool dumpAll = (aSizeToDump == 0) ? ETrue : EFalse; sl@0: sl@0: //Because the btrace buffer is a circular one, we need to save it in two parts sl@0: //this corrosponds to how we read it sl@0: TUint8* data; sl@0: TUint sizeOfPartRead; sl@0: TInt spaceRemaining = aSizeToDump; sl@0: sl@0: //This structure will be filled after the first pass and cached so by the time we ARE writing it will sl@0: //contain the data we want sl@0: aSizeDumped+=iTrace.GetSize(); sl@0: iTrace.Serialize(*iWriter); sl@0: sl@0: //read first part sl@0: TInt err = BTrace::Control(BTrace::ECtrlCrashReadFirst,&data,&sizeOfPartRead); sl@0: sl@0: while(KErrNone == err && sizeOfPartRead > 0) sl@0: { sl@0: TUint rawSize = 0; //how much of this read data want we to dump sl@0: sl@0: if(dumpAll) sl@0: { sl@0: rawSize = sizeOfPartRead; sl@0: } sl@0: else //Otherwise see what room is left for dumpage sl@0: { sl@0: rawSize = ((sizeOfPartRead + iTrace.iSizeOfMemory) > aSizeToDump) ? spaceRemaining : sizeOfPartRead; sl@0: } sl@0: sl@0: //Only relevant if restricting the dump sl@0: if(spaceRemaining <= 0 && !dumpAll) sl@0: break; sl@0: sl@0: TPtrC8 ptr(data, rawSize); sl@0: err = LogRawData(ptr, memDumped); sl@0: if(KErrNone != err) sl@0: { sl@0: CLTRACE1("Logging Raw data failed - [%d]", err); sl@0: err = BTrace::Control(BTrace::ECtrlCrashReadNext,&data,&sizeOfPartRead); sl@0: continue; sl@0: } sl@0: sl@0: aSizeDumped+=memDumped; sl@0: sl@0: iTrace.iSizeOfMemory += rawSize; sl@0: iTrace.iNumberOfParts++; sl@0: spaceRemaining -= rawSize; sl@0: sl@0: err = BTrace::Control(BTrace::ECtrlCrashReadNext,&data,&sizeOfPartRead); sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: * Logs the data in a TRawData struct sl@0: * @param aData sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return One of the OS wide codes sl@0: */ sl@0: TInt SCMDataSave::LogRawData(const TDesC8& aData, TUint& aSizeDumped) sl@0: { sl@0: TRawData theData; sl@0: theData.iLength = aData.Length(); sl@0: theData.iData.Set(const_cast(aData.Ptr()), aData.Length(), aData.Length()); sl@0: sl@0: aSizeDumped+=theData.GetSize(); sl@0: return theData.Serialize(*iWriter); sl@0: } sl@0: sl@0: sl@0: /** sl@0: * Logs the kernels heap and returns the size dumped via aSizeDumped sl@0: * @param aSizeDumped Holds the size of the data dumped sl@0: * @return sl@0: */ sl@0: TInt SCMDataSave::LogKernelHeap(TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: sl@0: TInt32 heapLoc = 0; sl@0: TInt32 heapSize = 0; sl@0: TInt32 err = FindKernelHeap(heapLoc, heapSize); sl@0: if(KErrNone == err) sl@0: { sl@0: return LogMemory((TUint8*)heapLoc, heapSize, &Kern::CurrentThread(), aSizeDumped); sl@0: } sl@0: sl@0: CLTRACE1("\tCouldnt find the kernel heap: [%d]", err); sl@0: return err; sl@0: } sl@0: sl@0: /** sl@0: * Iterates the object containers and finds the kernel heap sl@0: * @param aHeapLocation Contains the memory location of the kernel heap sl@0: * @param aHeapSize Contains the size of the Heap sl@0: * @return One of the OS wide codes sl@0: */ sl@0: TInt SCMDataSave::FindKernelHeap(TInt32& aHeapLocation, TInt32& aHeapSize) sl@0: { sl@0: LOG_CONTEXT sl@0: sl@0: //Get Chunk object container sl@0: DObjectCon* objectContainer = Kern::Containers()[EChunk]; sl@0: if(objectContainer == NULL) sl@0: { sl@0: CLTRACE("\tFailed to get object container for the chunks"); sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: //Must check the mutex on this is ok otherwise the data will be in an inconsistent state sl@0: if(objectContainer->Lock()->iHoldCount) sl@0: { sl@0: CLTRACE("\tChunk Container is in an inconsistant state"); sl@0: return KErrCorrupt; sl@0: } sl@0: sl@0: TInt numObjects = objectContainer->Count(); sl@0: sl@0: for(TInt cnt = 0; cnt< numObjects; cnt ++) sl@0: { sl@0: DChunk* candidateHeapChunk = (DChunk*)(*objectContainer)[cnt]; sl@0: sl@0: //Get the objects name sl@0: TBuf8 name; sl@0: candidateHeapChunk->TraceAppendFullName(name,EFalse); sl@0: sl@0: if(name == KKernelHeapChunkName) sl@0: { sl@0: #ifndef __MEMMODEL_FLEXIBLE__ sl@0: aHeapLocation = (TInt32)candidateHeapChunk->iBase; sl@0: #else sl@0: aHeapLocation = (TInt32)candidateHeapChunk->iFixedBase; sl@0: #endif sl@0: sl@0: aHeapSize = candidateHeapChunk->iSize; sl@0: sl@0: return KErrNone; sl@0: } sl@0: } sl@0: sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: /** sl@0: * This logs the variant specific descriptor data to the crash log sl@0: * @param aSizeDumped records how much was dumped by this function sl@0: * @return one of the OS wide codes sl@0: */ sl@0: TInt SCMDataSave::LogVariantSpecificData(TUint& aSizeDumped) sl@0: { sl@0: LOG_CONTEXT sl@0: sl@0: aSizeDumped = 0; sl@0: sl@0: //Change this descriptor as required for your needs sl@0: _LIT(KVariantSpecificData, "This is the variant specific data. Put your own here"); sl@0: sl@0: TVariantSpecificData varData; sl@0: varData.iSize = KVariantSpecificData().Size(); sl@0: sl@0: TInt err = varData.Serialize(*iWriter); sl@0: if(KErrNone != err) sl@0: { sl@0: CLTRACE1("\tLogging variant specific data failed with code [%d]", err); sl@0: return err; sl@0: } sl@0: aSizeDumped+=varData.GetSize(); sl@0: sl@0: TUint rawDataSize = 0; sl@0: err = LogRawData(KVariantSpecificData(), rawDataSize); sl@0: if(KErrNone != err) sl@0: { sl@0: CLTRACE1("\tLogging variant specific data failed with code [%d]", err); sl@0: return err; sl@0: } sl@0: sl@0: aSizeDumped+=rawDataSize; sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: /** sl@0: * This method is the callback used by MPhysicalWriterImpl interface sl@0: * if the TCachedByteStreamWriter is configured to use this interface sl@0: * the callback avoids the need for temp buffers & can interface directly with the sl@0: * flash writer methods sl@0: * @param aData - data to write sl@0: * @param aLen - length of data to write sl@0: * @param aPos - writers internal position sl@0: */ sl@0: void SCMDataSave::DoPhysicalWrite(TAny* aData, TInt aPos, TInt aLen) sl@0: { sl@0: if(iPerformChecksum) sl@0: { sl@0: iChecksum.ChecksumBlock((TUint8*)aData, aLen); sl@0: } sl@0: sl@0: if( this->iWriteSelect == EWriteComm) sl@0: { sl@0: WriteUart((TUint8*)aData, aLen); sl@0: } sl@0: else // EWriteFlash sl@0: { sl@0: Write(aData, aLen); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: * Writes data to Flash sl@0: * @param aSomething Pointer to the data sl@0: * @param aSize Size of the data sl@0: */ sl@0: void SCMDataSave::Write(const TAny* aSomething, TInt aSize) sl@0: { sl@0: TPtrC8 data((const TUint8 *)aSomething, aSize); sl@0: sl@0: TInt written = 0; sl@0: sl@0: WriteCrashFlash(iByteCount, written, data); sl@0: iByteCount+= written; sl@0: } sl@0: sl@0: /** sl@0: * Writes a descriptor to the crash flash sl@0: * @param aPos Position in flash to write sl@0: * @param aSize Holds the size of the data written after the call sl@0: * @param aBuffer Descriptor to write sl@0: */ sl@0: void SCMDataSave::WriteCrashFlash(TInt aPos, TInt& aSize, const TDesC8& aBuffer) sl@0: { sl@0: //Set write position in the flash sl@0: iFlash->SetWritePos(aPos); sl@0: iFlash->Write(aBuffer); sl@0: sl@0: //get bytes written sl@0: aSize += iFlash->BytesWritten(); sl@0: sl@0: if(aSize != aBuffer.Length()) sl@0: { sl@0: CLTRACE2("(SCMDataSave::WriteCrashFlash) Over the limit aSize = %d aBuffer.Length() = %d", sl@0: aSize, aBuffer.Length()); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: * Writes a descriptor via serial sl@0: * @param aDes Descriptor to write sl@0: */ sl@0: void SCMDataSave::WriteUart(const TDesC8& aDes) sl@0: { sl@0: WriteUart(aDes.Ptr(), aDes.Length()); sl@0: } sl@0: sl@0: /** sl@0: * Writes data via serial sl@0: * @param aData Data to write sl@0: * @param aSize Size of data to write sl@0: */ sl@0: void SCMDataSave::WriteUart(const TUint8* aData, TInt aSize) sl@0: { sl@0: OMAP* assp = ((OMAP*)Arch::TheAsic()); sl@0: TOmapDbgPrt* dbg = assp->DebugPort(); sl@0: sl@0: if (dbg) sl@0: { sl@0: for(TInt i=0;iDebugOutput(*(aData+i)); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: CLTRACE("SCMDataSave::WriteUart ERROR - dbg was null"); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: * Setter for the current number of bytes written for this crash log sl@0: * If aByte is not word aligned, it will be rounded up to be so sl@0: * @param aByte Current bytes written sl@0: */ sl@0: void SCMDataSave::SetByteCount(TInt aByte) sl@0: { sl@0: //ensure aligned sl@0: if(aByte % iWriter->GetCacheSize() == 0) sl@0: { sl@0: iByteCount = aByte; sl@0: } sl@0: else sl@0: { sl@0: iByteCount = aByte + (iWriter->GetCacheSize() - (aByte % iWriter->GetCacheSize())); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: * Gets the output target selection sl@0: * @return TScmWriteSelect output target selection sl@0: * @param void sl@0: */ sl@0: SCMDataSave::TWriteSelect SCMDataSave::GetWriteSelect() sl@0: { sl@0: return iWriteSelect; sl@0: } sl@0: sl@0: /** sl@0: * Sets the output target selection sl@0: * @return void sl@0: * @param TScmWriteSelect aWriteSelect output target selection sl@0: */ sl@0: void SCMDataSave::SetWriteSelect(SCMDataSave::TWriteSelect aWriteSelect) sl@0: { sl@0: iWriteSelect = aWriteSelect; sl@0: } sl@0: sl@0: /** sl@0: * Gets the amount of space remaining for the media of choice sl@0: * @return sl@0: */ sl@0: TUint SCMDataSave::SpaceRemaining() sl@0: { sl@0: TInt currentPosition = iWriter->GetBytesWritten() + iStartingPointForCrash; sl@0: sl@0: return MaxLogSize() - currentPosition; sl@0: } sl@0: sl@0: /** sl@0: * To find the max size of a log for a given media sl@0: * @return the max size of a log for a given media sl@0: */ sl@0: TUint SCMDataSave::MaxLogSize() sl@0: { sl@0: //see what write media is being used sl@0: switch(GetWriteSelect()) sl@0: { sl@0: case EWriteFlash: sl@0: { sl@0: return KMaxCrashLogSize; sl@0: } sl@0: case EWriteComm: sl@0: { sl@0: return 0xFFFFFFFF; sl@0: } sl@0: default: sl@0: { sl@0: return 0; sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: * Records the offset in the flash partition where this crash begins sl@0: * @param aStart Offset in flash sl@0: */ sl@0: void SCMDataSave::SetCrashStartingPoint(TUint32 aStart) sl@0: { sl@0: iStartingPointForCrash = aStart; sl@0: } sl@0: sl@0: //eof sl@0: