perfsrv/piprofiler/plugins/GeneralsPlugin/src/GppSamplerImpl.cpp
author hgs
Tue, 26 Oct 2010 16:20:32 +0300
changeset 62 1c2bb2fc7c87
parent 51 98307c651589
permissions -rw-r--r--
201043
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
20
hgs
parents:
diff changeset
     1
/*
hgs
parents:
diff changeset
     2
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
hgs
parents:
diff changeset
     3
* All rights reserved.
hgs
parents:
diff changeset
     4
* This component and the accompanying materials are made available
hgs
parents:
diff changeset
     5
* under the terms of "Eclipse Public License v1.0"
hgs
parents:
diff changeset
     6
* which accompanies this distribution, and is available
hgs
parents:
diff changeset
     7
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
hgs
parents:
diff changeset
     8
*
hgs
parents:
diff changeset
     9
* Initial Contributors:
hgs
parents:
diff changeset
    10
* Nokia Corporation - initial contribution.
hgs
parents:
diff changeset
    11
*
hgs
parents:
diff changeset
    12
* Contributors:
hgs
parents:
diff changeset
    13
*
hgs
parents:
diff changeset
    14
* Description:  
hgs
parents:
diff changeset
    15
*
hgs
parents:
diff changeset
    16
*/
hgs
parents:
diff changeset
    17
hgs
parents:
diff changeset
    18
hgs
parents:
diff changeset
    19
#include <piprofiler/ProfilerVersion.h>
hgs
parents:
diff changeset
    20
#include <piprofiler/ProfilerTraces.h>
hgs
parents:
diff changeset
    21
#include <kern_priv.h>
hgs
parents:
diff changeset
    22
#include <arm.h>
hgs
parents:
diff changeset
    23
hgs
parents:
diff changeset
    24
#include "GppSamplerImpl.h"
hgs
parents:
diff changeset
    25
hgs
parents:
diff changeset
    26
extern TUint*		IntStackPtr();
hgs
parents:
diff changeset
    27
#define	TAG(obj)	(*(TUint32*)&(obj.iAsyncDeleteNext))
hgs
parents:
diff changeset
    28
62
hgs
parents: 51
diff changeset
    29
#ifndef __SMP__
hgs
parents: 51
diff changeset
    30
extern TUint IDFCRunning();
hgs
parents: 51
diff changeset
    31
#endif
hgs
parents: 51
diff changeset
    32
hgs
parents: 51
diff changeset
    33
#ifdef __SMP__
hgs
parents: 51
diff changeset
    34
static TSpinLock CpuSpinLock = TSpinLock(TSpinLock::EOrderGenericIrqLow3);
hgs
parents: 51
diff changeset
    35
#endif
hgs
parents: 51
diff changeset
    36
20
hgs
parents:
diff changeset
    37
// properties for ISA task parsing
hgs
parents:
diff changeset
    38
const TUid KIsaPropertyCat={0x2001E5AD};
hgs
parents:
diff changeset
    39
enum TIsaPropertyKeys
hgs
parents:
diff changeset
    40
	{
hgs
parents:
diff changeset
    41
	EIsaPropertyIsaTaskParserStatus = 1,
hgs
parents:
diff changeset
    42
	EIsaPropertyIsaTaskAddressStart,
hgs
parents:
diff changeset
    43
	EIsaPropertyIsaTaskAddressEnd,
hgs
parents:
diff changeset
    44
	EIsaPropertyIsaTaskAddress,
hgs
parents:
diff changeset
    45
	EIsaPropertyIsaOsTaskRunningAddress,
hgs
parents:
diff changeset
    46
	EIsaPropertyIsaTaskParsedName
hgs
parents:
diff changeset
    47
	};
hgs
parents:
diff changeset
    48
hgs
parents:
diff changeset
    49
hgs
parents:
diff changeset
    50
DGppSamplerImpl::DGppSamplerImpl()
hgs
parents:
diff changeset
    51
	{
hgs
parents:
diff changeset
    52
	LOGTEXT("GppSamplerImpl::GppSamplerImpl");
62
hgs
parents: 51
diff changeset
    53
#ifndef __SMP__
20
hgs
parents:
diff changeset
    54
	iInterruptStack = (TUint*)IntStackPtr();
62
hgs
parents: 51
diff changeset
    55
#endif
hgs
parents: 51
diff changeset
    56
	
20
hgs
parents:
diff changeset
    57
	LOGTEXT("GppSamplerImpl::GppSamplerImpl - attaching to properties");
hgs
parents:
diff changeset
    58
62
hgs
parents: 51
diff changeset
    59
	TInt err(iIsaStartAddr.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskAddressStart));
20
hgs
parents:
diff changeset
    60
	if(err != KErrNone)
hgs
parents:
diff changeset
    61
		LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskAddressStart not available"); 
hgs
parents:
diff changeset
    62
	err = iIsaEndAddr.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskAddressEnd);
hgs
parents:
diff changeset
    63
	if(err != KErrNone)
hgs
parents:
diff changeset
    64
		LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskAddressEnd not available"); 
hgs
parents:
diff changeset
    65
	err = iIsaPluginStatus.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskParserStatus);
hgs
parents:
diff changeset
    66
	if(err != KErrNone)
hgs
parents:
diff changeset
    67
		LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskParserStatus not available"); 
hgs
parents:
diff changeset
    68
	err = iIsaOsTaskRunning.Attach(KIsaPropertyCat, EIsaPropertyIsaOsTaskRunningAddress);
hgs
parents:
diff changeset
    69
	if(err != KErrNone)
hgs
parents:
diff changeset
    70
		LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaOsTaskRunningAddress not available"); 
hgs
parents:
diff changeset
    71
	
hgs
parents:
diff changeset
    72
	PROFILER_ISA_TASK_NAMES
hgs
parents:
diff changeset
    73
	
62
hgs
parents: 51
diff changeset
    74
	NKern::LockSystem();
hgs
parents: 51
diff changeset
    75
	// get status of ISA plug-in
hgs
parents: 51
diff changeset
    76
	TPropertyStatus status;
hgs
parents: 51
diff changeset
    77
    if(iIsaPluginStatus.GetStatus(status))
hgs
parents: 51
diff changeset
    78
        {
hgs
parents: 51
diff changeset
    79
        iIsaPluginStatus.Get(iIsaStatus);
hgs
parents: 51
diff changeset
    80
        LOGSTRING2("GppSamplerImpl::Reset - ISA plug-in status %d", iIsaStatus);
hgs
parents: 51
diff changeset
    81
        }
hgs
parents: 51
diff changeset
    82
	NKern::UnlockSystem();
20
hgs
parents:
diff changeset
    83
	Reset();
hgs
parents:
diff changeset
    84
	}
hgs
parents:
diff changeset
    85
hgs
parents:
diff changeset
    86
DGppSamplerImpl::~DGppSamplerImpl() 
hgs
parents:
diff changeset
    87
	{
hgs
parents:
diff changeset
    88
	iIsaStartAddr.Close();
hgs
parents:
diff changeset
    89
	iIsaEndAddr.Close();
hgs
parents:
diff changeset
    90
	iIsaPluginStatus.Close();
hgs
parents:
diff changeset
    91
	iIsaOsTaskRunning.Close();
hgs
parents:
diff changeset
    92
	}
hgs
parents:
diff changeset
    93
hgs
parents:
diff changeset
    94
void DGppSamplerImpl::Reset()
hgs
parents:
diff changeset
    95
	{
62
hgs
parents: 51
diff changeset
    96
    LOGSTRING("GppSamplerImpl::Reset() - entry");
20
hgs
parents:
diff changeset
    97
	iLastPc = 0;
42
hgs
parents: 20
diff changeset
    98
	iLastThread = 0xfffffffe;
20
hgs
parents:
diff changeset
    99
	iRepeat = 0;
hgs
parents:
diff changeset
   100
	iIsaStatus = 0;
hgs
parents:
diff changeset
   101
	iIsaStart = 0;
hgs
parents:
diff changeset
   102
	iIsaEnd = 0;
hgs
parents:
diff changeset
   103
//	isaOsTaskRunningAddr = 0;
62
hgs
parents: 51
diff changeset
   104
	iPrevTS = NKern::TickCount(); // get the system tick value for sync purposes 
20
hgs
parents:
diff changeset
   105
	
hgs
parents:
diff changeset
   106
	// in SMP start time common with all CPUs, provided by DGeneralsDriver class
hgs
parents:
diff changeset
   107
#ifndef __SMP__
62
hgs
parents: 51
diff changeset
   108
	iStartTime = ( iPrevTS & 0xfffffffc );
hgs
parents: 51
diff changeset
   109
#else
hgs
parents: 51
diff changeset
   110
	iStartTime = ( iPrevTS & 0xfffffff0 );
20
hgs
parents:
diff changeset
   111
#endif
hgs
parents:
diff changeset
   112
	
62
hgs
parents: 51
diff changeset
   113
	TInt osAddr(0);
20
hgs
parents:
diff changeset
   114
	
62
hgs
parents: 51
diff changeset
   115
	LOGSTRING("GppSamplerImpl::Reset - getting status");
20
hgs
parents:
diff changeset
   116
	
hgs
parents:
diff changeset
   117
	if(iIsaStatus > 0)
hgs
parents:
diff changeset
   118
		{
62
hgs
parents: 51
diff changeset
   119
		LOGSTRING("GppSamplerImpl::Reset - get isa start address");
20
hgs
parents:
diff changeset
   120
		iIsaStartAddr.Get(iIsaStart);
62
hgs
parents: 51
diff changeset
   121
		LOGSTRING("GppSamplerImpl::Reset - get isa end address");
20
hgs
parents:
diff changeset
   122
		iIsaEndAddr.Get(iIsaEnd);
62
hgs
parents: 51
diff changeset
   123
		LOGSTRING("GppSamplerImpl::Reset - get isa os_task_running address");
20
hgs
parents:
diff changeset
   124
		iIsaOsTaskRunning.Get(osAddr);
hgs
parents:
diff changeset
   125
		isaOsTaskRunningAddr = reinterpret_cast<TInt*>(osAddr);
hgs
parents:
diff changeset
   126
		LOGSTRING2("GppSamplerImpl::Reset - got isa os_task_running address 0x%X", osAddr);
hgs
parents:
diff changeset
   127
		}
hgs
parents:
diff changeset
   128
	
62
hgs
parents: 51
diff changeset
   129
	LOGSTRING("GppSamplerImpl::Reset - initializing isa task list");
20
hgs
parents:
diff changeset
   130
hgs
parents:
diff changeset
   131
	iIsaSample = false;
hgs
parents:
diff changeset
   132
	
62
hgs
parents: 51
diff changeset
   133
	for(TInt i(0);i<256;i++)
20
hgs
parents:
diff changeset
   134
		knownIsaTasks[i] = -1;
hgs
parents:
diff changeset
   135
	
hgs
parents:
diff changeset
   136
	knownIsaTaskCount = 0;
hgs
parents:
diff changeset
   137
    
hgs
parents:
diff changeset
   138
	iCpuSelector = 0x3;
hgs
parents:
diff changeset
   139
#ifndef __SMP__
hgs
parents:
diff changeset
   140
    iMask =  0xfffffffc;
hgs
parents:
diff changeset
   141
#else
hgs
parents:
diff changeset
   142
    iMask =  0xfffffff0;
hgs
parents:
diff changeset
   143
    switch(iCpuNumber)
hgs
parents:
diff changeset
   144
        {
hgs
parents:
diff changeset
   145
        case 0:
hgs
parents:
diff changeset
   146
            iCpuSelector = 0x1;
hgs
parents:
diff changeset
   147
            break;
hgs
parents:
diff changeset
   148
        case 1:
hgs
parents:
diff changeset
   149
            iCpuSelector = 0x2;
hgs
parents:
diff changeset
   150
            break;
hgs
parents:
diff changeset
   151
        case 2:
hgs
parents:
diff changeset
   152
            iCpuSelector = 0x4;
hgs
parents:
diff changeset
   153
            break;
hgs
parents:
diff changeset
   154
        case 3:
hgs
parents:
diff changeset
   155
            iCpuSelector = 0x8;
hgs
parents:
diff changeset
   156
            break;
hgs
parents:
diff changeset
   157
        }
hgs
parents:
diff changeset
   158
#endif
62
hgs
parents: 51
diff changeset
   159
    LOGSTRING("GppSamplerImpl::Reset() - exit");
20
hgs
parents:
diff changeset
   160
	}
hgs
parents:
diff changeset
   161
hgs
parents:
diff changeset
   162
TUint8* DGppSamplerImpl::EncodeTag(TUint8* aPtr)
hgs
parents:
diff changeset
   163
//
hgs
parents:
diff changeset
   164
// Encode a tag and version to the trace data. This allows the offline analyser to 
hgs
parents:
diff changeset
   165
// identify the sample data.
hgs
parents:
diff changeset
   166
//
hgs
parents:
diff changeset
   167
{	
hgs
parents:
diff changeset
   168
	_LIT(KGppSamplerVersion,"Bappea_GPP_V");
hgs
parents:
diff changeset
   169
	_LIT(KProfilerVersion,"#Prof#");
hgs
parents:
diff changeset
   170
	_LIT(KSamplerVersion,"#Samp#");
hgs
parents:
diff changeset
   171
#ifdef __SMP__
hgs
parents:
diff changeset
   172
	_LIT(KCPUNumberText,"#CPU#");
hgs
parents:
diff changeset
   173
#endif
hgs
parents:
diff changeset
   174
	
hgs
parents:
diff changeset
   175
	TBuf<64> buf;
hgs
parents:
diff changeset
   176
	buf.Zero();
hgs
parents:
diff changeset
   177
	buf.Append(KGppSamplerVersion);
hgs
parents:
diff changeset
   178
	buf.Append(PROFILER_GPP_SAMPLER_VERSION);
hgs
parents:
diff changeset
   179
	buf.Append(KProfilerVersion);
hgs
parents:
diff changeset
   180
	buf.Append(PROFILER_VERSION_SHORT);	
hgs
parents:
diff changeset
   181
	buf.Append(KSamplerVersion);
hgs
parents:
diff changeset
   182
	buf.Append(PROFILER_SAMPLER_VERSION);
hgs
parents:
diff changeset
   183
#ifdef __SMP__
hgs
parents:
diff changeset
   184
	buf.Append(KCPUNumberText);
hgs
parents:
diff changeset
   185
	buf.AppendNum(iCpuNumber);
hgs
parents:
diff changeset
   186
#endif
hgs
parents:
diff changeset
   187
	aPtr = EncodeText(aPtr, buf);
hgs
parents:
diff changeset
   188
	return aPtr;
hgs
parents:
diff changeset
   189
}
hgs
parents:
diff changeset
   190
hgs
parents:
diff changeset
   191
TUint8* DGppSamplerImpl::EncodeInt(TUint8* aPtr,TInt aValue)
hgs
parents:
diff changeset
   192
{
hgs
parents:
diff changeset
   193
	LOGSTRING2("Encoding int 0x%x",aPtr);
hgs
parents:
diff changeset
   194
hgs
parents:
diff changeset
   195
	LOGSTRING2("TIint = 0x%x",aValue);
62
hgs
parents: 51
diff changeset
   196
#ifdef __SMP__
hgs
parents: 51
diff changeset
   197
	TInt intState(0);
hgs
parents: 51
diff changeset
   198
	intState = __SPIN_LOCK_IRQSAVE(CpuSpinLock);
hgs
parents: 51
diff changeset
   199
#endif
20
hgs
parents:
diff changeset
   200
	TUint byte;
hgs
parents:
diff changeset
   201
	for (;;)
hgs
parents:
diff changeset
   202
		{
hgs
parents:
diff changeset
   203
		byte = aValue & 0x7f;
hgs
parents:
diff changeset
   204
		if ((aValue >> 6) == (aValue >> 7))
hgs
parents:
diff changeset
   205
			break;
hgs
parents:
diff changeset
   206
		aValue >>= 7;
hgs
parents:
diff changeset
   207
		*aPtr++ = byte;
hgs
parents:
diff changeset
   208
		}
hgs
parents:
diff changeset
   209
	*aPtr++ = byte | 0x80;
62
hgs
parents: 51
diff changeset
   210
#ifdef __SMP__
hgs
parents: 51
diff changeset
   211
	__SPIN_UNLOCK_IRQRESTORE(CpuSpinLock, intState);
hgs
parents: 51
diff changeset
   212
#endif
20
hgs
parents:
diff changeset
   213
	LOGSTRING2("Encoded int 0x%x",aPtr);
hgs
parents:
diff changeset
   214
hgs
parents:
diff changeset
   215
	return aPtr;
hgs
parents:
diff changeset
   216
}
hgs
parents:
diff changeset
   217
hgs
parents:
diff changeset
   218
TUint8* DGppSamplerImpl::EncodeUint(TUint8* aPtr,TUint aValue)
hgs
parents:
diff changeset
   219
{
hgs
parents:
diff changeset
   220
	LOGSTRING2("Encoding Uint 0x%x",aPtr);
hgs
parents:
diff changeset
   221
hgs
parents:
diff changeset
   222
	LOGSTRING2("TUint = 0x%x",aValue);
hgs
parents:
diff changeset
   223
62
hgs
parents: 51
diff changeset
   224
#ifdef __SMP__
hgs
parents: 51
diff changeset
   225
	TInt intState(0);
hgs
parents: 51
diff changeset
   226
	intState = __SPIN_LOCK_IRQSAVE(CpuSpinLock);
hgs
parents: 51
diff changeset
   227
#endif
20
hgs
parents:
diff changeset
   228
	TUint byte;
hgs
parents:
diff changeset
   229
	for (;;)
hgs
parents:
diff changeset
   230
		{
hgs
parents:
diff changeset
   231
		byte = aValue & 0x7f;
hgs
parents:
diff changeset
   232
		aValue >>= 7;
hgs
parents:
diff changeset
   233
		if (aValue == 0)
hgs
parents:
diff changeset
   234
			break;
hgs
parents:
diff changeset
   235
		*aPtr++ = byte;
hgs
parents:
diff changeset
   236
		}
hgs
parents:
diff changeset
   237
	*aPtr++ = byte | 0x80;
62
hgs
parents: 51
diff changeset
   238
#ifdef __SMP__
hgs
parents: 51
diff changeset
   239
	__SPIN_UNLOCK_IRQRESTORE(CpuSpinLock, intState);
hgs
parents: 51
diff changeset
   240
#endif
20
hgs
parents:
diff changeset
   241
	LOGSTRING2("Encoded Uint 0x%x",aPtr);
hgs
parents:
diff changeset
   242
hgs
parents:
diff changeset
   243
	return aPtr;
hgs
parents:
diff changeset
   244
}
hgs
parents:
diff changeset
   245
hgs
parents:
diff changeset
   246
TUint8* DGppSamplerImpl::EncodeText(TUint8* aPtr, const TDesC& aDes)
hgs
parents:
diff changeset
   247
//
hgs
parents:
diff changeset
   248
// Encode a descriptor into the data stream
hgs
parents:
diff changeset
   249
// This is currently limited to a descriptor that is up to 255 characters in length,
hgs
parents:
diff changeset
   250
// and Unicode characters are truncated to 8 bits
hgs
parents:
diff changeset
   251
//
hgs
parents:
diff changeset
   252
{
hgs
parents:
diff changeset
   253
	LOGSTRING2("Encoding text 0x%x",aPtr);
62
hgs
parents: 51
diff changeset
   254
	TInt len(aDes.Length());
hgs
parents: 51
diff changeset
   255
#ifdef __SMP__
hgs
parents: 51
diff changeset
   256
	TInt intState(0);
hgs
parents: 51
diff changeset
   257
	intState = __SPIN_LOCK_IRQSAVE(CpuSpinLock);
hgs
parents: 51
diff changeset
   258
#endif
20
hgs
parents:
diff changeset
   259
	*aPtr++ = TUint8(len);
hgs
parents:
diff changeset
   260
	const TText* p = aDes.Ptr();
hgs
parents:
diff changeset
   261
	while (--len >= 0)
hgs
parents:
diff changeset
   262
		{
hgs
parents:
diff changeset
   263
		*aPtr++ = TUint8(*p++);
hgs
parents:
diff changeset
   264
		}
62
hgs
parents: 51
diff changeset
   265
#ifdef __SMP__
hgs
parents: 51
diff changeset
   266
	__SPIN_UNLOCK_IRQRESTORE(CpuSpinLock, intState);
hgs
parents: 51
diff changeset
   267
#endif
20
hgs
parents:
diff changeset
   268
	LOGSTRING2("Encoded text 0x%x",aPtr);
hgs
parents:
diff changeset
   269
	return aPtr;
hgs
parents:
diff changeset
   270
}
hgs
parents:
diff changeset
   271
hgs
parents:
diff changeset
   272
hgs
parents:
diff changeset
   273
TUint8* DGppSamplerImpl::EncodeName(TUint8* aPtr, DObject& aObject,TUint32 id)
hgs
parents:
diff changeset
   274
//
hgs
parents:
diff changeset
   275
// Encode the name of a kernel object
hgs
parents:
diff changeset
   276
//
hgs
parents:
diff changeset
   277
{
hgs
parents:
diff changeset
   278
	LOGSTRING2("Encoding name 0x%x",aPtr);
hgs
parents:
diff changeset
   279
	TBuf8<0x5f> name;
hgs
parents:
diff changeset
   280
	aObject.TraceAppendName(name,false);
62
hgs
parents: 51
diff changeset
   281
	LOGSTRING2("DGppSamplerImpl::EncodeName() - new process/thread found: %S", &name);
hgs
parents: 51
diff changeset
   282
	
20
hgs
parents:
diff changeset
   283
	if(id != 0xffffffff)
hgs
parents:
diff changeset
   284
	{
hgs
parents:
diff changeset
   285
		name.Append('[');
hgs
parents:
diff changeset
   286
		name.AppendNum(id,EHex);
hgs
parents:
diff changeset
   287
		name.Append(']');
hgs
parents:
diff changeset
   288
	}
hgs
parents:
diff changeset
   289
	else
hgs
parents:
diff changeset
   290
	{
hgs
parents:
diff changeset
   291
		name.Append('[');
hgs
parents:
diff changeset
   292
		name.AppendNum((TUint32)((void*)&(((DThread*)&aObject)->iNThread)),EHex);
hgs
parents:
diff changeset
   293
		name.Append(']');
hgs
parents:
diff changeset
   294
	}
hgs
parents:
diff changeset
   295
hgs
parents:
diff changeset
   296
	aPtr = EncodeText(aPtr,name);
hgs
parents:
diff changeset
   297
	LOGSTRING2("Encoded name 0x%x",aPtr);
hgs
parents:
diff changeset
   298
	return aPtr;
hgs
parents:
diff changeset
   299
}
hgs
parents:
diff changeset
   300
hgs
parents:
diff changeset
   301
TUint8* DGppSamplerImpl::EncodeThread(TUint8* aPtr, DThread& aThread)
hgs
parents:
diff changeset
   302
//
hgs
parents:
diff changeset
   303
// Encode a thread name in the data stream.
hgs
parents:
diff changeset
   304
// The thread is identified by its name, and the identity of its owning process.
hgs
parents:
diff changeset
   305
// If the process has not been identified in the data stream already, it's name is
hgs
parents:
diff changeset
   306
// also encoded.
hgs
parents:
diff changeset
   307
//
hgs
parents:
diff changeset
   308
{
hgs
parents:
diff changeset
   309
	LOGSTRING2("Encoding thread 0x%x",aPtr);	
hgs
parents:
diff changeset
   310
62
hgs
parents: 51
diff changeset
   311
	DProcess& p(*aThread.iOwningProcess);
20
hgs
parents:
diff changeset
   312
	
hgs
parents:
diff changeset
   313
	aPtr = EncodeUint(aPtr, p.iId);
hgs
parents:
diff changeset
   314
hgs
parents:
diff changeset
   315
#ifdef __SMP__
hgs
parents:
diff changeset
   316
    // check if first time founding
hgs
parents:
diff changeset
   317
    if ((TAG(p) & iMask) != iStartTime)
hgs
parents:
diff changeset
   318
        {
hgs
parents:
diff changeset
   319
        // mark tagged for this CPU
hgs
parents:
diff changeset
   320
        TAG(p) = (iStartTime | iCpuSelector);
hgs
parents:
diff changeset
   321
hgs
parents:
diff changeset
   322
        // The thread is 'unknown' to this sample, so encode the thread name
hgs
parents:
diff changeset
   323
        aPtr = EncodeName(aPtr, p, p.iId);     
hgs
parents:
diff changeset
   324
        }
hgs
parents:
diff changeset
   325
    // check if thread appeared already on this CPU
hgs
parents:
diff changeset
   326
    else if((TAG(p) & iCpuSelector) != iCpuSelector)
hgs
parents:
diff changeset
   327
        {
hgs
parents:
diff changeset
   328
        TAG(p) = (TAG(p) | iCpuSelector);
hgs
parents:
diff changeset
   329
        // The thread is 'unknown' to this sample, so encode the thread name
hgs
parents:
diff changeset
   330
        aPtr = EncodeName(aPtr, p, p.iId);     
hgs
parents:
diff changeset
   331
        }
62
hgs
parents: 51
diff changeset
   332
    else
hgs
parents: 51
diff changeset
   333
        {
hgs
parents: 51
diff changeset
   334
        
hgs
parents: 51
diff changeset
   335
        }
20
hgs
parents:
diff changeset
   336
#else
hgs
parents:
diff changeset
   337
	if (TAG(p) != iStartTime)
hgs
parents:
diff changeset
   338
	    {
hgs
parents:
diff changeset
   339
		TAG(p) = iStartTime;
hgs
parents:
diff changeset
   340
		// Provide the name matching this process ID
hgs
parents:
diff changeset
   341
		aPtr = EncodeName(aPtr, p, p.iId);
hgs
parents:
diff changeset
   342
	    }
hgs
parents:
diff changeset
   343
#endif	    
hgs
parents:
diff changeset
   344
	aPtr = EncodeName(aPtr, aThread,0xffffffff);
hgs
parents:
diff changeset
   345
	
hgs
parents:
diff changeset
   346
	LOGSTRING2("Encoded thread 0x%x",aPtr);	
hgs
parents:
diff changeset
   347
hgs
parents:
diff changeset
   348
	return aPtr;
hgs
parents:
diff changeset
   349
    }
hgs
parents:
diff changeset
   350
hgs
parents:
diff changeset
   351
TUint8* DGppSamplerImpl::EncodeRepeat(TUint8* aPtr)
hgs
parents:
diff changeset
   352
//
hgs
parents:
diff changeset
   353
// Encode a repeated sequence of samples
hgs
parents:
diff changeset
   354
//
hgs
parents:
diff changeset
   355
{
hgs
parents:
diff changeset
   356
	LOGSTRING2("Encoding repeat, 0x%x",iRepeat);	
hgs
parents:
diff changeset
   357
hgs
parents:
diff changeset
   358
	aPtr = EncodeInt(aPtr, 0);
hgs
parents:
diff changeset
   359
	aPtr = EncodeUint(aPtr, iRepeat);
hgs
parents:
diff changeset
   360
	iRepeat = 0;
hgs
parents:
diff changeset
   361
hgs
parents:
diff changeset
   362
	LOGSTRING2("Encoded repeat, 0x%x",iRepeat);	
hgs
parents:
diff changeset
   363
hgs
parents:
diff changeset
   364
	return aPtr;
hgs
parents:
diff changeset
   365
}
hgs
parents:
diff changeset
   366
hgs
parents:
diff changeset
   367
TInt DGppSamplerImpl::CreateFirstSample()
hgs
parents:
diff changeset
   368
{
hgs
parents:
diff changeset
   369
	LOGTEXT("GppSamplerImpl::CreateFirstSample");
hgs
parents:
diff changeset
   370
	Reset();
hgs
parents:
diff changeset
   371
62
hgs
parents: 51
diff changeset
   372
	TUint8* w(this->tempBuf);
20
hgs
parents:
diff changeset
   373
	w = EncodeTag(w);
hgs
parents:
diff changeset
   374
62
hgs
parents: 51
diff changeset
   375
	TInt length(w-tempBuf);
20
hgs
parents:
diff changeset
   376
hgs
parents:
diff changeset
   377
	LOGSTRING2("TAG encoded, length %d",length);
hgs
parents:
diff changeset
   378
	return length;
hgs
parents:
diff changeset
   379
}
hgs
parents:
diff changeset
   380
hgs
parents:
diff changeset
   381
TBool DGppSamplerImpl::IsaTaskKnown(TUint8 task)
hgs
parents:
diff changeset
   382
{
62
hgs
parents: 51
diff changeset
   383
	for(TInt i(0);i<256;i++)
20
hgs
parents:
diff changeset
   384
	{
hgs
parents:
diff changeset
   385
		if(knownIsaTasks[i] == -1)
hgs
parents:
diff changeset
   386
		{
hgs
parents:
diff changeset
   387
			knownIsaTasks[i] = task;
hgs
parents:
diff changeset
   388
			knownIsaTaskCount++;
hgs
parents:
diff changeset
   389
			return false;
hgs
parents:
diff changeset
   390
		}
hgs
parents:
diff changeset
   391
		else if(knownIsaTasks[i] == task)
hgs
parents:
diff changeset
   392
		{
hgs
parents:
diff changeset
   393
			return true;
hgs
parents:
diff changeset
   394
		}
hgs
parents:
diff changeset
   395
	}
hgs
parents:
diff changeset
   396
hgs
parents:
diff changeset
   397
	return false;
hgs
parents:
diff changeset
   398
}
hgs
parents:
diff changeset
   399
hgs
parents:
diff changeset
   400
TUint8* DGppSamplerImpl::EncodeIsaTask(TUint8* aPtr, TUint task)
hgs
parents:
diff changeset
   401
hgs
parents:
diff changeset
   402
{
hgs
parents:
diff changeset
   403
	LOGSTRING2("Encoding ISA task 0x%x",aPtr);	
hgs
parents:
diff changeset
   404
hgs
parents:
diff changeset
   405
	aPtr = EncodeUint(aPtr,task);
hgs
parents:
diff changeset
   406
	// use the task name as the process name
hgs
parents:
diff changeset
   407
	aPtr = EncodeIsaName(aPtr,task,true);
hgs
parents:
diff changeset
   408
	// then encode the task name
hgs
parents:
diff changeset
   409
	aPtr = EncodeIsaName(aPtr,task,false);
hgs
parents:
diff changeset
   410
	
hgs
parents:
diff changeset
   411
	LOGSTRING2("Encoded ISA task 0x%x",aPtr);	
hgs
parents:
diff changeset
   412
hgs
parents:
diff changeset
   413
	return aPtr;
hgs
parents:
diff changeset
   414
}
hgs
parents:
diff changeset
   415
hgs
parents:
diff changeset
   416
TUint8* DGppSamplerImpl::EncodeIsaName(TUint8* aPtr, TUint task,TBool process)
hgs
parents:
diff changeset
   417
//
hgs
parents:
diff changeset
   418
// Encode a descriptor into the data stream
hgs
parents:
diff changeset
   419
// This is currently limited to a descriptor that is up to 255 characters in length,
hgs
parents:
diff changeset
   420
// and Unicode characters are truncated to 8 bits
hgs
parents:
diff changeset
   421
//
hgs
parents:
diff changeset
   422
{
hgs
parents:
diff changeset
   423
	TBuf8<256> aDes;
hgs
parents:
diff changeset
   424
	
hgs
parents:
diff changeset
   425
	if(iIsaStatus > 0)
hgs
parents:
diff changeset
   426
		{
hgs
parents:
diff changeset
   427
		// resolve the isa task name from the task name array
hgs
parents:
diff changeset
   428
		if((task-100000) < PROFILER_ISA_OS_TASK_AMOUNT && process == false)
hgs
parents:
diff changeset
   429
			{
hgs
parents:
diff changeset
   430
			aDes.Append(isaTaskNames[(task-100000)]);
hgs
parents:
diff changeset
   431
			}
hgs
parents:
diff changeset
   432
		else
hgs
parents:
diff changeset
   433
			{
hgs
parents:
diff changeset
   434
			aDes.Append(_L8("NativeOS_Task"));
hgs
parents:
diff changeset
   435
			}
hgs
parents:
diff changeset
   436
		}
hgs
parents:
diff changeset
   437
	else
hgs
parents:
diff changeset
   438
		{
hgs
parents:
diff changeset
   439
		aDes.Append(_L8("NativeOS_Task"));
hgs
parents:
diff changeset
   440
		}
hgs
parents:
diff changeset
   441
	
hgs
parents:
diff changeset
   442
	aDes.Append('[');
hgs
parents:
diff changeset
   443
	aDes.AppendNum((task-100000),EHex);
hgs
parents:
diff changeset
   444
	aDes.Append(']');
hgs
parents:
diff changeset
   445
hgs
parents:
diff changeset
   446
	LOGSTRING2("Encoding ISA name 0x%x",aPtr);
62
hgs
parents: 51
diff changeset
   447
	TInt len(aDes.Length());
20
hgs
parents:
diff changeset
   448
	*aPtr++ = TUint8(len);
62
hgs
parents: 51
diff changeset
   449
	const TText* p(aDes.Ptr());
20
hgs
parents:
diff changeset
   450
	while (--len >= 0)
hgs
parents:
diff changeset
   451
		{
hgs
parents:
diff changeset
   452
		*aPtr++ = TUint8(*p++);
hgs
parents:
diff changeset
   453
		}
hgs
parents:
diff changeset
   454
hgs
parents:
diff changeset
   455
	LOGSTRING2("Encoded ISA name 0x%x",aPtr);
hgs
parents:
diff changeset
   456
	return aPtr;
hgs
parents:
diff changeset
   457
}
hgs
parents:
diff changeset
   458
hgs
parents:
diff changeset
   459
hgs
parents:
diff changeset
   460
TInt DGppSamplerImpl::SampleImpl()
hgs
parents:
diff changeset
   461
//
hgs
parents:
diff changeset
   462
// ISR for the profile timer
hgs
parents:
diff changeset
   463
// This extracts the thread and PC that was current when the interrupt went off and
hgs
parents:
diff changeset
   464
// encodes it into the sample data buffer. If enough data has been generated, the
hgs
parents:
diff changeset
   465
// DFC is triggered to complete a read request
hgs
parents:
diff changeset
   466
//
hgs
parents:
diff changeset
   467
    {
62
hgs
parents: 51
diff changeset
   468
    NThread* pN(NKern::CurrentThread());
hgs
parents: 51
diff changeset
   469
    TUint8* w(this->tempBuf);
hgs
parents: 51
diff changeset
   470
    //TInt currCpu(NKern::CurrentCpu());
hgs
parents: 51
diff changeset
   471
    TInt mySampleStartTime(NKern::TickCount()); // get the system tick value for sync purposes, in ms
hgs
parents: 51
diff changeset
   472
//    TInt tickdiff(mySampleStartTime-iPrevTS);
hgs
parents: 51
diff changeset
   473
    
hgs
parents: 51
diff changeset
   474
    if (Kern::NThreadToDThread(pN)==NULL)
hgs
parents: 51
diff changeset
   475
        {
hgs
parents: 51
diff changeset
   476
        LOGSTRING("Kern::NThreadToDThread is null");
hgs
parents: 51
diff changeset
   477
        }
20
hgs
parents:
diff changeset
   478
62
hgs
parents: 51
diff changeset
   479
    else
hgs
parents: 51
diff changeset
   480
        {
hgs
parents: 51
diff changeset
   481
        // check timestamp & compare to previous
hgs
parents: 51
diff changeset
   482
        // if out of sync, encode unknown::unknown thread
20
hgs
parents:
diff changeset
   483
62
hgs
parents: 51
diff changeset
   484
/*
hgs
parents: 51
diff changeset
   485
// In SMP environment there have been missing ticks. These prints are for helping debug this issue
hgs
parents: 51
diff changeset
   486
// but solution might not work.
hgs
parents: 51
diff changeset
   487
        if(tickdiff > 1)
hgs
parents: 51
diff changeset
   488
            {
hgs
parents: 51
diff changeset
   489
            Kern::Printf(("DGppSamplerImpl::SampleImpl() - Current time tick %d"), mySampleStartTime);
hgs
parents: 51
diff changeset
   490
            Kern::Printf(("DGppSamplerImpl::SampleImpl() - Previous time tick %d"), iPrevTS);
hgs
parents: 51
diff changeset
   491
//            Kern::Printf(("DGppSamplerImpl::SampleImpl() - Tick period %d"),NKern::TickPeriod());
hgs
parents: 51
diff changeset
   492
            Kern::Printf(("DGppSamplerImpl::SampleImpl() - Tick difference %d"),tickdiff);
hgs
parents: 51
diff changeset
   493
            Kern::Printf(("DGppSamplerImpl::SampleImpl() - Current cpu %d"),NKern::CurrentCpu());
hgs
parents: 51
diff changeset
   494
            }
hgs
parents: 51
diff changeset
   495
//        else
hgs
parents: 51
diff changeset
   496
//            {
hgs
parents: 51
diff changeset
   497
//            Kern::Printf(("DGppSamplerImpl::SampleImpl() - Tick differance <= 1: %d"),tickdiff);
hgs
parents: 51
diff changeset
   498
//            }
hgs
parents: 51
diff changeset
   499
*/
20
hgs
parents:
diff changeset
   500
62
hgs
parents: 51
diff changeset
   501
        TLinAddr pC;
hgs
parents: 51
diff changeset
   502
            
hgs
parents: 51
diff changeset
   503
#ifdef __SMP__
hgs
parents: 51
diff changeset
   504
        pC = Arm::IrqReturnAddress();
hgs
parents: 51
diff changeset
   505
#else
hgs
parents: 51
diff changeset
   506
        // get program counter of irq mode
hgs
parents: 51
diff changeset
   507
        pC = iInterruptStack[-1]; // ignore the low bit being set for THUMB mode - we use for something else
hgs
parents: 51
diff changeset
   508
#endif
hgs
parents: 51
diff changeset
   509
        //Kern::Printf(("pc value 0x%x sp 0x%x"),pc,iInterruptStack);
hgs
parents: 51
diff changeset
   510
        pC &= ~1;
hgs
parents: 51
diff changeset
   511
        TInt diff(pC - iLastPc);
hgs
parents: 51
diff changeset
   512
        iLastPc = pC;
hgs
parents: 51
diff changeset
   513
           
hgs
parents: 51
diff changeset
   514
        if(iIsaStatus > 0)
hgs
parents: 51
diff changeset
   515
            {
hgs
parents: 51
diff changeset
   516
            if((TUint32)pC > (TUint32)iIsaStart && (TUint32)pC < (TUint32)iIsaEnd)
hgs
parents: 51
diff changeset
   517
                {
hgs
parents: 51
diff changeset
   518
                iIsaSample = true;
hgs
parents: 51
diff changeset
   519
                }
hgs
parents: 51
diff changeset
   520
            else
hgs
parents: 51
diff changeset
   521
                {
hgs
parents: 51
diff changeset
   522
                iIsaSample = false;
hgs
parents: 51
diff changeset
   523
                }
hgs
parents: 51
diff changeset
   524
            }
hgs
parents: 51
diff changeset
   525
        
hgs
parents: 51
diff changeset
   526
//        if((TUint32)pC < 0x80000000 || (TUint32)pC > 0x8FFFFFFF)
hgs
parents: 51
diff changeset
   527
//            {
hgs
parents: 51
diff changeset
   528
//            //Kern::Printf("DGppSamplerImpl::SampleImpl() - wrong PC value, 0x%x", (TUint32)pC);
hgs
parents: 51
diff changeset
   529
//            iIsaSample = true;
hgs
parents: 51
diff changeset
   530
//            }
hgs
parents: 51
diff changeset
   531
        
hgs
parents: 51
diff changeset
   532
        // request for current thread from kernel
hgs
parents: 51
diff changeset
   533
        DThread& t(((DThread&)*Kern::NThreadToDThread(pN)));
hgs
parents: 51
diff changeset
   534
    
hgs
parents: 51
diff changeset
   535
        TUint tid;
hgs
parents: 51
diff changeset
   536
        TUint8 isaTask(0);
hgs
parents: 51
diff changeset
   537
        if(iIsaSample)
hgs
parents: 51
diff changeset
   538
            {
hgs
parents: 51
diff changeset
   539
            //Kern::Printf(("Reading ISA task number from 0x%x"),isaOsTaskRunningAddr);
hgs
parents: 51
diff changeset
   540
            // if we don't get reasonable ISA address to read, skip ISA task handling
hgs
parents: 51
diff changeset
   541
            if(isaOsTaskRunningAddr == 0)
hgs
parents: 51
diff changeset
   542
                {
hgs
parents: 51
diff changeset
   543
                tid = 100000; // to tell the difference from SOS threads
hgs
parents: 51
diff changeset
   544
                iIsaSample = false;
hgs
parents: 51
diff changeset
   545
                }
hgs
parents: 51
diff changeset
   546
            else    // normal ISA task parsing process
hgs
parents: 51
diff changeset
   547
                {
hgs
parents: 51
diff changeset
   548
                isaTask = *isaOsTaskRunningAddr;
hgs
parents: 51
diff changeset
   549
                LOGSTRING2("ISA task = %d",isaTask);
hgs
parents: 51
diff changeset
   550
                tid = isaTask;
hgs
parents: 51
diff changeset
   551
                // this will make sure we don't mix ISA tasks and normal tasks
hgs
parents: 51
diff changeset
   552
                tid += 100000;
hgs
parents: 51
diff changeset
   553
                }
hgs
parents: 51
diff changeset
   554
            }
hgs
parents: 51
diff changeset
   555
        else
hgs
parents: 51
diff changeset
   556
            {
hgs
parents: 51
diff changeset
   557
            tid = t.iId;
hgs
parents: 51
diff changeset
   558
            }
hgs
parents: 51
diff changeset
   559
    
hgs
parents: 51
diff changeset
   560
        if (tid != iLastThread)
hgs
parents: 51
diff changeset
   561
            {
hgs
parents: 51
diff changeset
   562
            diff |= 1;    // Change of thread is marked in the low bit of the PC difference
hgs
parents: 51
diff changeset
   563
            }
hgs
parents: 51
diff changeset
   564
        TUint rp(iRepeat);
hgs
parents: 51
diff changeset
   565
        if (diff == 0)
hgs
parents: 51
diff changeset
   566
            {
hgs
parents: 51
diff changeset
   567
            iRepeat = rp + 1; // Identical sample, bump up the repeat count
hgs
parents: 51
diff changeset
   568
            }
hgs
parents: 51
diff changeset
   569
        else
hgs
parents: 51
diff changeset
   570
            {
hgs
parents: 51
diff changeset
   571
            if (rp)
hgs
parents: 51
diff changeset
   572
                {
hgs
parents: 51
diff changeset
   573
                w = EncodeRepeat(w); // Encode the repeat data
hgs
parents: 51
diff changeset
   574
                }
hgs
parents: 51
diff changeset
   575
            w = EncodeInt(w, diff);   // Encode the PC difference
hgs
parents: 51
diff changeset
   576
            //if (diff & 0x1 == 1)
hgs
parents: 51
diff changeset
   577
            if (diff & 1)
hgs
parents: 51
diff changeset
   578
                {
hgs
parents: 51
diff changeset
   579
//                LOGSTRING3("Encode pc diff curr cpu %d diff %d", currCpu, diff);
hgs
parents: 51
diff changeset
   580
                if(iIsaSample)   // Encode the new thread ID
hgs
parents: 51
diff changeset
   581
                    {
hgs
parents: 51
diff changeset
   582
                    iLastThread = tid;
hgs
parents: 51
diff changeset
   583
//                    LOGSTRING2("Encode thread id UINT curr cpu %d", currCpu);
hgs
parents: 51
diff changeset
   584
                    w = EncodeUint(w,tid);
hgs
parents: 51
diff changeset
   585
                    if(!this->IsaTaskKnown(isaTask))
hgs
parents: 51
diff changeset
   586
                        {
hgs
parents: 51
diff changeset
   587
                        //Kern::Printf("Encode isa task");
hgs
parents: 51
diff changeset
   588
                        w = EncodeIsaTask(w,iLastThread);
hgs
parents: 51
diff changeset
   589
                        }
hgs
parents: 51
diff changeset
   590
                    //LOGSTRING2("Sample total length: %d",w-tempBuf);
hgs
parents: 51
diff changeset
   591
                    TInt length = w-tempBuf;
hgs
parents: 51
diff changeset
   592
                    return length;  // encoded isa task, return here
hgs
parents: 51
diff changeset
   593
                    }
hgs
parents: 51
diff changeset
   594
                iLastThread = tid;
hgs
parents: 51
diff changeset
   595
//                LOGSTRING2("Encode lastthread id UinT curr cpu %d", currCpu);
hgs
parents: 51
diff changeset
   596
                w = EncodeUint(w, tid);
hgs
parents: 51
diff changeset
   597
    
hgs
parents: 51
diff changeset
   598
#ifdef __SMP__
hgs
parents: 51
diff changeset
   599
                // iStartTime format: 0xXXXXXXX0, the last byte set to zero
hgs
parents: 51
diff changeset
   600
                // iMask =  0xfffffff0(0b111....1110000), last bits reserved for the CPUs bit0=CPU0 etc.
hgs
parents: 51
diff changeset
   601
                // iCpuSelector = 0x1(0b0001), 0x2(0b0010), 0x4(0b0100) or 0x8(0b1000), to use against the mask
hgs
parents: 51
diff changeset
   602
                //#define  TAG(obj)    (*(TUint32*)&(obj.iAsyncDeleteNext))
hgs
parents: 51
diff changeset
   603
                // check first time founding
hgs
parents: 51
diff changeset
   604
                LOGSTRING4("TAG(t) 0x%x, iMask 0x%x, AND 0x%x ",TAG(t),iMask, (TAG(t)& iMask));
hgs
parents: 51
diff changeset
   605
                if ((TAG(t) & iMask) != iStartTime)
hgs
parents: 51
diff changeset
   606
                    {
hgs
parents: 51
diff changeset
   607
                    // mark tagged for this CPU
hgs
parents: 51
diff changeset
   608
                    TAG(t) = (iStartTime | iCpuSelector);
hgs
parents: 51
diff changeset
   609
                    
hgs
parents: 51
diff changeset
   610
                    // The thread is 'unknown' to this sample, so encode the thread name
hgs
parents: 51
diff changeset
   611
//                    LOGSTRING2("Encode SMP thread start time curr cpu %d", currCpu);
hgs
parents: 51
diff changeset
   612
                    w = EncodeThread(w, t);
hgs
parents: 51
diff changeset
   613
                    }
hgs
parents: 51
diff changeset
   614
                // check if thread appeared on this CPU
hgs
parents: 51
diff changeset
   615
                else if((TAG(t) & iCpuSelector) != iCpuSelector)
hgs
parents: 51
diff changeset
   616
                    {
hgs
parents: 51
diff changeset
   617
                    TAG(t) = (TAG(t) | iCpuSelector);
hgs
parents: 51
diff changeset
   618
                    // The thread is 'unknown' to this sample, so encode the thread name
hgs
parents: 51
diff changeset
   619
//                    LOGSTRING2("Encode SMP thread cpu selector curr cpu %d", currCpu);
hgs
parents: 51
diff changeset
   620
                    w = EncodeThread(w, t);
hgs
parents: 51
diff changeset
   621
                    }
hgs
parents: 51
diff changeset
   622
#else
hgs
parents: 51
diff changeset
   623
                // check if tag has not been set, neither original nor 
hgs
parents: 51
diff changeset
   624
                if ((TAG(t) & 0xfffffffc) != iStartTime)
hgs
parents: 51
diff changeset
   625
                    {
hgs
parents: 51
diff changeset
   626
                    TAG(t) = ((TAG(t) & 0x3) | iStartTime);
hgs
parents: 51
diff changeset
   627
                    // The thread is 'unknown' to this sample, so encode the thread name
hgs
parents: 51
diff changeset
   628
                    w = EncodeThread(w, t);
hgs
parents: 51
diff changeset
   629
                    }
hgs
parents: 51
diff changeset
   630
        
hgs
parents: 51
diff changeset
   631
#endif
hgs
parents: 51
diff changeset
   632
                }
hgs
parents: 51
diff changeset
   633
            }
hgs
parents: 51
diff changeset
   634
        }
20
hgs
parents:
diff changeset
   635
62
hgs
parents: 51
diff changeset
   636
    TInt length(w-tempBuf);
hgs
parents: 51
diff changeset
   637
    LOGSTRING2("Sample total length: %d",length);
hgs
parents: 51
diff changeset
   638
    iPrevTS = mySampleStartTime;  // get the system tick value for sync purposes 
hgs
parents: 51
diff changeset
   639
    return length;
hgs
parents: 51
diff changeset
   640
    }
20
hgs
parents:
diff changeset
   641