| 0 |      1 | // Copyright (c) 2005-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".
 | 
|  |      7 | //
 | 
|  |      8 | // Initial Contributors:
 | 
|  |      9 | // Nokia Corporation - initial contribution.
 | 
|  |     10 | //
 | 
|  |     11 | // Contributors:
 | 
|  |     12 | //
 | 
|  |     13 | // Description:
 | 
|  |     14 | // e32\drivers\trace\btracec.cpp
 | 
|  |     15 | // 
 | 
|  |     16 | //
 | 
|  |     17 | 
 | 
|  |     18 | #include <e32std.h>
 | 
|  |     19 | #include <e32std_private.h>
 | 
|  |     20 | #include "d32btrace.h"
 | 
|  |     21 | #include <e32svr.h>
 | 
|  |     22 | #include <e32atomics.h>
 | 
|  |     23 | 
 | 
|  |     24 | 
 | 
|  |     25 | void Panic(TInt aPanicNum)
 | 
|  |     26 | 	{
 | 
|  |     27 | 	_LIT(KRBTracePanic,"RBTrace");
 | 
|  |     28 | 	User::Panic(KRBTracePanic,aPanicNum);
 | 
|  |     29 | 	}
 | 
|  |     30 | 
 | 
|  |     31 | EXPORT_C TInt RBTrace::Open()
 | 
|  |     32 | 	{
 | 
|  |     33 | 	_LIT(KBTraceLddName,"btracex");
 | 
|  |     34 | 	TInt r = User::LoadLogicalDevice(KBTraceLddName);
 | 
|  |     35 | 	if(r!=KErrNone && r!=KErrAlreadyExists)
 | 
|  |     36 | 		return r;
 | 
|  |     37 | 	r = DoCreate( Name(), TVersion(), KNullUnit, NULL, NULL, EOwnerThread);
 | 
|  |     38 | 	if(r==KErrNone)
 | 
|  |     39 | 		{
 | 
|  |     40 | 		r = OpenChunk();
 | 
|  |     41 | 		if(r!=KErrNone)
 | 
|  |     42 | 			Close();
 | 
|  |     43 | 		}
 | 
|  |     44 | 	return r;
 | 
|  |     45 | 	};
 | 
|  |     46 | 
 | 
|  |     47 | 
 | 
|  |     48 | TInt RBTrace::OpenChunk()
 | 
|  |     49 | 	{
 | 
|  |     50 | 	TInt r = iDataChunk.SetReturnedHandle(DoControl(EOpenBuffer));
 | 
|  |     51 | 	if(r==KErrNone)
 | 
|  |     52 | 		iBuffer = (TBTraceBuffer*)iDataChunk.Base();
 | 
|  |     53 | 	iLastGetDataSize = 0;
 | 
|  |     54 | 	return r;
 | 
|  |     55 | 	}
 | 
|  |     56 | 
 | 
|  |     57 | 
 | 
|  |     58 | void RBTrace::CloseChunk()
 | 
|  |     59 | 	{
 | 
|  |     60 | 	iLastGetDataSize = 0;
 | 
|  |     61 | 	iBuffer = NULL;
 | 
|  |     62 | 	iDataChunk.Close();
 | 
|  |     63 | 	}
 | 
|  |     64 | 
 | 
|  |     65 | 
 | 
|  |     66 | EXPORT_C void RBTrace::Close()
 | 
|  |     67 | 	{
 | 
|  |     68 | 	CloseChunk();
 | 
|  |     69 | 	RBusLogicalChannel::Close();
 | 
|  |     70 | 	}
 | 
|  |     71 | 
 | 
|  |     72 | 
 | 
|  |     73 | EXPORT_C TInt RBTrace::BufferSize()
 | 
|  |     74 | 	{
 | 
|  |     75 | 	if(!iDataChunk.Handle())
 | 
|  |     76 | 		return 0;
 | 
|  |     77 | 	return iBuffer->iEnd;
 | 
|  |     78 | 	}
 | 
|  |     79 | 
 | 
|  |     80 | 
 | 
|  |     81 | EXPORT_C TInt RBTrace::ResizeBuffer(TInt aSize)
 | 
|  |     82 | 	{
 | 
|  |     83 | 	CloseChunk();
 | 
|  |     84 | 	TInt r = DoControl(EResizeBuffer,(TAny*)aSize);
 | 
|  |     85 | 	if(r==KErrNone)
 | 
|  |     86 | 		r = OpenChunk();
 | 
|  |     87 | 	return r;
 | 
|  |     88 | 	}
 | 
|  |     89 | 
 | 
|  |     90 | 
 | 
|  |     91 | EXPORT_C void RBTrace::Empty()
 | 
|  |     92 | 	{
 | 
|  |     93 | 	TBTraceBuffer* buffer = iBuffer;
 | 
|  |     94 | 	TUint32 mode = __e32_atomic_swp_acq32(&buffer->iMode, 0);	/* read original mode and disable trace */
 | 
|  |     95 | 	while(__e32_atomic_load_acq32(&buffer->iGeneration) & 1)	/* wait for trace handler to complete */
 | 
|  |     96 | 		{ /* should really __chill() but not available user side */ }
 | 
|  |     97 | 	buffer->iTail = buffer->iHead;
 | 
|  |     98 | 	__e32_atomic_store_ord32(&buffer->iMode, mode);
 | 
|  |     99 | 	}
 | 
|  |    100 | 
 | 
|  |    101 | 
 | 
|  |    102 | EXPORT_C TUint RBTrace::Mode()
 | 
|  |    103 | 	{
 | 
|  |    104 | 	return iBuffer->iMode;
 | 
|  |    105 | 	}
 | 
|  |    106 | 
 | 
|  |    107 | 
 | 
|  |    108 | EXPORT_C void RBTrace::SetMode(TUint aMode)
 | 
|  |    109 | 	{
 | 
|  |    110 | 	iLastGetDataSize = 0;
 | 
|  |    111 | 	__e32_atomic_store_ord32(&iBuffer->iMode, aMode);
 | 
|  |    112 | 	}
 | 
|  |    113 | 
 | 
|  |    114 | 
 | 
|  |    115 | EXPORT_C TInt RBTrace::SetFilter(TUint aCategory, TInt aValue)
 | 
|  |    116 | 	{
 | 
|  |    117 | 	return (TInt)DoControl(ESetFilter,(TAny*)aCategory,(TAny*)aValue);
 | 
|  |    118 | 	}
 | 
|  |    119 | 
 | 
|  |    120 | 
 | 
|  |    121 | EXPORT_C TInt RBTrace::SetFilter2(TUint32 aUid, TBool aValue)
 | 
|  |    122 | 	{
 | 
|  |    123 | 	return (TInt)DoControl(ESetFilter2,(TAny*)aUid,(TAny*)aValue);
 | 
|  |    124 | 	}
 | 
|  |    125 | 
 | 
|  |    126 | 
 | 
|  |    127 | EXPORT_C TInt RBTrace::SetFilter2(const TUint32* aUids, TInt aNumUids)
 | 
|  |    128 | 	{
 | 
|  |    129 | 	return (TInt)DoControl(ESetFilter2Array,(TAny*)aUids,(TAny*)aNumUids);
 | 
|  |    130 | 	}
 | 
|  |    131 | 
 | 
|  |    132 | 
 | 
|  |    133 | EXPORT_C TInt RBTrace::SetFilter2(TInt aGlobalFilter)
 | 
|  |    134 | 	{
 | 
|  |    135 | 	return DoControl(ESetFilter2Global,(TAny*)aGlobalFilter);
 | 
|  |    136 | 	}
 | 
|  |    137 | 
 | 
|  |    138 | 
 | 
|  |    139 | EXPORT_C TInt RBTrace::Filter2(TUint32*& aUids, TInt& aGlobalFilter)
 | 
|  |    140 | 	{
 | 
|  |    141 | 	TInt count = (TInt)DoControl(EGetFilter2Part1,&aUids,&aGlobalFilter);
 | 
|  |    142 | 	if(count<=0)
 | 
|  |    143 | 		{
 | 
|  |    144 | 		aUids = 0;
 | 
|  |    145 | 		return count;
 | 
|  |    146 | 		}
 | 
|  |    147 | 	aUids = (TUint32*)User::Alloc(count*sizeof(TUint32));
 | 
|  |    148 | 	if(!aUids)
 | 
|  |    149 | 		return KErrNoMemory;
 | 
|  |    150 | 	DoControl(EGetFilter2Part2,aUids,(TAny*)count);
 | 
|  |    151 | 	return count;
 | 
|  |    152 | 	}
 | 
|  |    153 | 
 | 
|  |    154 | 
 | 
|  |    155 | EXPORT_C TInt RBTrace::GetData(TUint8*& aData)
 | 
|  |    156 | 	{
 | 
|  |    157 | 	TInt size = iBuffer->GetData(aData);
 | 
|  |    158 | 	iLastGetDataSize = size;
 | 
|  |    159 | 	return size;
 | 
|  |    160 | 	}
 | 
|  |    161 | 
 | 
|  |    162 | EXPORT_C void RBTrace::DataUsed()
 | 
|  |    163 | 	{
 | 
|  |    164 | 	TBTraceBuffer* buffer = iBuffer;
 | 
|  |    165 | 	if(!(buffer->iMode&RBTrace::EFreeRunning))
 | 
|  |    166 | 		{
 | 
|  |    167 | 		/* Make sure change to iTail is not observed before the trace data reads
 | 
|  |    168 | 			which preceded the call to this function. */
 | 
|  |    169 | 		__e32_memory_barrier();
 | 
|  |    170 | 		buffer->iTail += iLastGetDataSize;
 | 
|  |    171 | 		}
 | 
|  |    172 | 	iLastGetDataSize = 0;
 | 
|  |    173 | 	}
 | 
|  |    174 | 
 | 
|  |    175 | 
 | 
|  |    176 | EXPORT_C void RBTrace::RequestData(TRequestStatus& aStatus, TInt aSize)
 | 
|  |    177 | 	{
 | 
|  |    178 | 	if(aSize<0)
 | 
|  |    179 | 		aSize = 0;
 | 
|  |    180 | 	aStatus = KRequestPending;
 | 
|  |    181 | 	if(aSize || iBuffer->iHead==iBuffer->iTail)
 | 
|  |    182 | 		DoControl(ERequestData,&aStatus,(TAny*)aSize);
 | 
|  |    183 | 	else
 | 
|  |    184 | 		{
 | 
|  |    185 | 		TRequestStatus* s = &aStatus;
 | 
|  |    186 | 		User::RequestComplete(s,KErrNone);
 | 
|  |    187 | 		}
 | 
|  |    188 | 	}
 | 
|  |    189 | 
 | 
|  |    190 | 
 | 
|  |    191 | EXPORT_C void RBTrace::CancelRequestData()
 | 
|  |    192 | 	{
 | 
|  |    193 | 	DoControl(ECancelRequestData);
 | 
|  |    194 | 	}
 | 
|  |    195 | 
 | 
|  |    196 | EXPORT_C TBool RBTrace::SetTimestamp2Enabled(TBool aEnable)
 | 
|  |    197 | 	{
 | 
|  |    198 | 	return (TBool)DoControl(ESetTimestamp2Enabled, (TAny*)aEnable);
 | 
|  |    199 | 	}
 | 
|  |    200 | 
 | 
|  |    201 | /**
 | 
|  |    202 | Find out how much data is available.
 | 
|  |    203 | @param aData Set to the buffer offset where the available trace data is located.
 | 
|  |    204 | @param aTail Set to the the original value of the iTail pointer
 | 
|  |    205 | @return Number of bytes of trace data, or an error.
 | 
|  |    206 | */
 | 
|  |    207 | TInt TBTraceBuffer::Data(TUint& aData, TUint& aTail)
 | 
|  |    208 | 	{
 | 
|  |    209 | 	TUint head, tail, wrap;
 | 
|  |    210 | 	TUint32 g0;
 | 
|  |    211 | 	TInt retries=64;
 | 
|  |    212 | 	do	{
 | 
|  |    213 | 		if (--retries<0)
 | 
|  |    214 | 			return KErrInUse;
 | 
|  |    215 | 		// sleep every 8 tries to give the write a chance
 | 
|  |    216 | 		if (retries&7==0)
 | 
|  |    217 | 			User::AfterHighRes(1);
 | 
|  |    218 | 		g0 = iGeneration;
 | 
|  |    219 | 		__e32_memory_barrier();
 | 
|  |    220 | 		head = iHead;
 | 
|  |    221 | 		wrap = iWrap;
 | 
|  |    222 | 		tail = __e32_atomic_and_rlx32(&iTail, ~TUint32(1));
 | 
|  |    223 | 		__e32_memory_barrier();
 | 
|  |    224 | 		} while(iGeneration!=g0 || (g0&1));	// repeat until we get a consistent set
 | 
|  |    225 | 	tail &= ~1;
 | 
|  |    226 | 	aTail = tail;
 | 
|  |    227 | 	TUint end = head;
 | 
|  |    228 | 	if (head<tail)
 | 
|  |    229 | 		{
 | 
|  |    230 | 		end = wrap;
 | 
|  |    231 | 		if (tail>=end)
 | 
|  |    232 | 			{
 | 
|  |    233 | 			tail = iStart;
 | 
|  |    234 | 			end = head;
 | 
|  |    235 | 			}
 | 
|  |    236 | 		}
 | 
|  |    237 | 	aData = tail;
 | 
|  |    238 | 	return end - tail;
 | 
|  |    239 | 	}
 | 
|  |    240 | 
 | 
|  |    241 | 
 | 
|  |    242 | /**
 | 
|  |    243 | Adjust trace data size so it represents a whole number of trace records.
 | 
|  |    244 | @param aData The buffer offset where the available trace data is located.
 | 
|  |    245 | @param aSize The size of data at aTail. Must be >= KMaxBTraceRecordSize.
 | 
|  |    246 | @return An adjusted value for aSize.
 | 
|  |    247 | */
 | 
|  |    248 | TInt TBTraceBuffer::Adjust(TUint aData, TInt aSize)
 | 
|  |    249 | 	{
 | 
|  |    250 | 	TInt adjustedSize = (aSize&~3) - KMaxBTraceRecordSize;
 | 
|  |    251 | 	if (adjustedSize<0)
 | 
|  |    252 | 		Panic(0);
 | 
|  |    253 | 	volatile TUint8* recordOffsets = (volatile TUint8*)this + iRecordOffsets;
 | 
|  |    254 | 	adjustedSize += recordOffsets[(aData+adjustedSize)>>2]<<2;
 | 
|  |    255 | 	if (adjustedSize>aSize)
 | 
|  |    256 | 		Panic(1);
 | 
|  |    257 | 	return adjustedSize;
 | 
|  |    258 | 	}
 | 
|  |    259 | 
 | 
|  |    260 | 
 | 
|  |    261 | /**
 | 
|  |    262 | Update the stored tail offset.
 | 
|  |    263 | @param aOld The value which iTail is expected to have if no more overwrites have occurred
 | 
|  |    264 | @param aNew The new value for iTail
 | 
|  |    265 | @return aNew on success, 0 if the previous tail value had changed before we could update it.
 | 
|  |    266 | */
 | 
|  |    267 | TUint TBTraceBuffer::UpdateTail(TUint32 aOld, TUint32 aNew)
 | 
|  |    268 | 	{
 | 
|  |    269 | 	if (__e32_atomic_cas_rel32(&iTail, &aOld, aNew))
 | 
|  |    270 | 		return aNew;	// if iTail==aOld, set iTail=aNew and return aNew
 | 
|  |    271 | 	return 0;	// else return 0
 | 
|  |    272 | 	}
 | 
|  |    273 | 
 | 
|  |    274 | 
 | 
|  |    275 | /**
 | 
|  |    276 | Copy data out of the main trace buffer into the 'copy buffer'.
 | 
|  |    277 | This may fail if the data is overwritten before it hase been successfully copied.
 | 
|  |    278 | @param aData The buffer offset where the available trace data is located.
 | 
|  |    279 | @param aTail The value which iTail is expected to have if no more overwrites have occurred
 | 
|  |    280 | @param aSize The size of data at aTail
 | 
|  |    281 | @return The number of bytes successfully copied.
 | 
|  |    282 | @post iBuffer.iTail has been updated to point to the trace record following those copied.
 | 
|  |    283 | */
 | 
|  |    284 | TInt TBTraceBuffer::CopyData(TUint aData, TUint aTail, TInt aSize)
 | 
|  |    285 | 	{
 | 
|  |    286 | 	// clip size to copy buffer
 | 
|  |    287 | 	TInt maxSize = iCopyBufferSize;
 | 
|  |    288 | 	if (aSize>maxSize)
 | 
|  |    289 | 		aSize = Adjust(aData,maxSize);
 | 
|  |    290 | 
 | 
|  |    291 | 	if (iTail & 1)
 | 
|  |    292 | 		return 0; // give up if data we're about to copy has been overwritten
 | 
|  |    293 | 
 | 
|  |    294 | 	memcpy((TUint8*)this+iCopyBuffer, (TUint8*)this+aData, aSize);
 | 
|  |    295 | 
 | 
|  |    296 | 	if (!UpdateTail(aTail, aData+aSize))
 | 
|  |    297 | 		return 0;
 | 
|  |    298 | 
 | 
|  |    299 | 	return aSize;
 | 
|  |    300 | 	}
 | 
|  |    301 | 
 | 
|  |    302 | 
 | 
|  |    303 | /**
 | 
|  |    304 | Get pointer to as much contiguous trace data as is available.
 | 
|  |    305 | @param aData Pointer to the first byte of trace data.
 | 
|  |    306 | @return The number of bytes of trace data available at aData.
 | 
|  |    307 | */
 | 
|  |    308 | TInt TBTraceBuffer::GetData(TUint8*& aData)
 | 
|  |    309 | 	{
 | 
|  |    310 | 	TInt retries = 4;
 | 
|  |    311 | 
 | 
|  |    312 | 	// get availabe data...
 | 
|  |    313 | 	TUint data, tail;
 | 
|  |    314 | 	TInt size = Data(data, tail);
 | 
|  |    315 | 	if (!size)
 | 
|  |    316 | 		return size; // no data available
 | 
|  |    317 | 
 | 
|  |    318 | 	if (!(iMode & RBTrace::EFreeRunning))
 | 
|  |    319 | 		{
 | 
|  |    320 | 		// if we got an error from Data but aren't in free-running mode, something has
 | 
|  |    321 | 		// been interrupting the writing thread for some time while it has interrupts
 | 
|  |    322 | 		// turned off. give up.
 | 
|  |    323 | 		if (size<0)
 | 
|  |    324 | 			return 0;
 | 
|  |    325 | 		// were not in free-running mode, so we can leave the data where it is...
 | 
|  |    326 | 		aData = (TUint8*)this + data;
 | 
|  |    327 | 		iTail = data;	// OK since iTail never updated by kernel in this mode
 | 
|  |    328 | 		return size;
 | 
|  |    329 | 		}
 | 
|  |    330 | 
 | 
|  |    331 | 	// if we couldn't get a consistent snapshot of the pointers, we need to disable
 | 
|  |    332 | 	// free running, otherwise we will stall for a very long time.
 | 
|  |    333 | 	if (size==KErrInUse)
 | 
|  |    334 | 		goto giveup;
 | 
|  |    335 | 
 | 
|  |    336 | 	// copy data to the copy buffer...
 | 
|  |    337 | 	aData = (TUint8*)this + iCopyBuffer;
 | 
|  |    338 | 	size = CopyData(data, tail, size);
 | 
|  |    339 | 	if (size)
 | 
|  |    340 | 		return size; // success
 | 
|  |    341 | 
 | 
|  |    342 | 	// data copy failed because new data was added during copy; this happens when the buffer
 | 
|  |    343 | 	// is full, so now we'll attempt to discard data to give us some breathing space...
 | 
|  |    344 | 	while(retries)
 | 
|  |    345 | 		{
 | 
|  |    346 | 		// see what data is available...
 | 
|  |    347 | 		size = Data(data, tail);
 | 
|  |    348 | 		if (!size)
 | 
|  |    349 | 			return size; // no data in buffer (shouldn't happen because buffer was full to start with)
 | 
|  |    350 | 		if (size==KErrInUse)
 | 
|  |    351 | 			goto giveup;
 | 
|  |    352 | 
 | 
|  |    353 | 		// discard a proportion of the data...
 | 
|  |    354 | 		TInt discard = iCopyBufferSize>>retries;
 | 
|  |    355 | 		if (discard>=size)
 | 
|  |    356 | 			discard = size;
 | 
|  |    357 | 		else
 | 
|  |    358 | 			discard = Adjust(data, discard); // make sure we only discard a whole number of trace records
 | 
|  |    359 | 		size -= discard;
 | 
|  |    360 | 		data = UpdateTail(tail, data+discard);
 | 
|  |    361 | 		if (!data)
 | 
|  |    362 | 			continue;	// tail was updated before we could discard, so try again
 | 
|  |    363 | 		if (!size)
 | 
|  |    364 | 			continue;	// we discarded everything - make sure and then exit
 | 
|  |    365 | 
 | 
|  |    366 | 		// try and copy remaining data...
 | 
|  |    367 | 		size = CopyData(data, data, size);
 | 
|  |    368 | 		if (size)
 | 
|  |    369 | 			break; // success!
 | 
|  |    370 | 
 | 
|  |    371 | 		// loop around for another go, discard more this time
 | 
|  |    372 | 		--retries;
 | 
|  |    373 | 		}
 | 
|  |    374 | 
 | 
|  |    375 | 	if (!size)
 | 
|  |    376 | 		{
 | 
|  |    377 | giveup:
 | 
|  |    378 | 		// we haven't managed to copy data, so give up and do it with free-running mode off...
 | 
|  |    379 | 		TUint32 mode = __e32_atomic_and_acq32(&iMode, ~(TUint32)RBTrace::EFreeRunning);	/* read original mode and disable free running */
 | 
|  |    380 | 		size = Data(data, tail);
 | 
|  |    381 | 		// as above: if we get an error here then something has been interrupting the writer
 | 
|  |    382 | 		// for an unreasonable time, give up.
 | 
|  |    383 | 		if (size<0)
 | 
|  |    384 | 			return 0;
 | 
|  |    385 | 		size = CopyData(data, tail, size);
 | 
|  |    386 | 		__e32_atomic_store_ord32(&iMode, mode);	/* restore original mode */
 | 
|  |    387 | 		}
 | 
|  |    388 | 
 | 
|  |    389 | 	// we discarded some data, so mark first trace record to indicate that some records are missing...
 | 
|  |    390 | 	aData[BTrace::EFlagsIndex] |= BTrace::EMissingRecord;
 | 
|  |    391 | 
 | 
|  |    392 | 	return size;
 | 
|  |    393 | 	}
 | 
|  |    394 | 
 | 
|  |    395 | 
 |