kerneltest/e32utils/analyse/analyse.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include "analyse.h"
#include "trace.h"
#include "tracer.h"
#include "distribution.h"
#include "activity.h"
#include "nonxip.h"

#ifdef __MSVCDOTNET__
#include <strstream>
#include <iomanip>
#else //!__MSVCDOTNET__
#include <strstrea.h>
#include <iomanip.h>
#endif //__MSVCDOTNET__

#include <ctype.h>

Analyse::TAction Analyse::sAction;
Analyse::TFormat Analyse::sFormat;
Analyse::TPartition Analyse::sPartition;
int Analyse::sOptions;
std::vector<const char*> Analyse::sTraces;
const char* Analyse::sRomFile;
const char* Analyse::sThread;
const char* Analyse::sDll;
const char* Analyse::sFunction;
unsigned Analyse::sBase;
unsigned Analyse::sLim;
unsigned Analyse::sBuckets = 100;
unsigned Analyse::sBucketSize;
double Analyse::sCutOff;
unsigned Analyse::sBeginSample;
unsigned Analyse::sEndSample = 0xffffffffu;


NonXIP gNonXIP; // 

//namespace {

void PartitionByDll::File(const char* aName)
	{
	iCurrentFile = aName;
	}

bool PartitionByDll::Symbol(const char*, PC aPc, int)
	{
	bool is_added = false;
	if (iCurrentFile)
		{
		if (iLastFile && Analyse::Match(iLastFile, iMatch))
			{
			Add(iLastFileAddress, aPc, iLastFile);
			is_added = true;
			}
		iLastFile = iCurrentFile;
		iLastFileAddress = aPc;
		iCurrentFile = 0;
		}
	return is_added;
	}


PartitionByFunction::PartitionByFunction(const char* aFile, const char* aFunction)
	:iFile(aFile), iFunction(aFunction), iActive(false)
	{}

void PartitionByFunction::File(const char* aName)
	{
	iActive = Analyse::Match(aName, iFile);
	}

bool PartitionByFunction::Symbol(const char* aSymbol, PC aPc, int aLength)
	{
	bool is_added = false;
	if (iActive && Analyse::Match(aSymbol, iFunction))
		{
		Add(aPc, aPc + aLength, aSymbol);
		is_added = true;
		}
	return is_added;
	}



class FindFunction : public SymbolFile::Parser
	{
public:
	FindFunction(const char* aFile, const char* aFunction);
	void File(const char* aName);
	bool Symbol(const char* aName, PC aPc, int aLength);
	void Done(PC aFirstPc=0, PC aLastPc=0, int aModuleId=0);
private:
	const char* iFile;
	const char* iFunction;
	bool iActive;
public:
	PC iPc;
	int iLength;
	};

FindFunction::FindFunction(const char* aFile, const char* aFunction)
	:iFile(aFile), iFunction(aFunction), iActive(false), iPc(0)
	{}

void FindFunction::File(const char* aName)
	{
	if (iPc == 0)
		iActive = Analyse::Match(aName, iFile);
	}

bool FindFunction::Symbol(const char* aSymbol, PC aPc, int aLength)
	{
	bool is_added = false;
	if (iPc == 0 && iActive && Analyse::Match(aSymbol, iFunction))
		{
		iPc = aPc;
		iLength = aLength;
		is_added = true;
		}
	return is_added;
	}

void FindFunction::Done(PC aFirstPc, PC aLastPc, int aModuleId)
	{}

//};	// local namepsace


// entry point

int main(int argc,char *argv[])
	{
	switch(Analyse::ProcessCommandLine(argc,argv))
		{
	case 1:
		Analyse::ExplainUsage();
		return 1;
	case 2:
		Analyse::ExplainConfigUsage();
		return 1;
		}
	Analyse::Run();
	return 0;
	}

// Class Analyse

void Analyse::Information()
	{
	cout << "\nEPOC Profile Analyser   Version " << MajorVersion << '.' \
		<< setw(2) << MinorVersion << "(build " << setw(3) << setfill('0') << Build \
		<< ")\nCopyright (c) Symbian Limited 2000. All rights reserved.\n\n" << flush;
	}

void Analyse::ExplainUsage()
	{
	Information();
	cout << "Usage:  Analyse [options] tracefile\n" \
			" -h            display this information\n" \
			" -l            generate a trace listing\n" \
			" -p            generate a profile distribution\n" \
			" -v            generate a activity trace\n" \
			" -r <symbols>  supply a Rom symbol file\n" \
			" -s<range>     restrict the profile to the samples specified\n" \
			"               This is specified either as <start>-<end> or\n" \
			"               as <start>+<length> in decimal\n" \
			" -n            include NULL thread\n" \
			" -t <thread>   profile threads matching the pattern\n" \
			" -d <dll>      profile DLL (or EXE) matching the pattern\n" \
			" -f <function> profile the function matching the pattern\n" \
			" -a<range>     profile the address range specified\n" \
			"               This is specified either as <start>-<end> or\n" \
			"               as <start>+<length> in hexadecimal\n" \
			" -bd           partition the profile by dll/exe\n" \
			" -bf           partition the profile by function\n" \
			" -bs<n>        partition the profile into buckets of size n\n" \
			" -bn<n>        partition the profile into approx. n buckets\n" \
			" -c<n>         set the cutoff value for discarding output\n" \
			" -m...         setformat options:\n" \
			"   p|s|x       use percentages/samples/excel for output\n" \
			"   z           output zero values instead of blanks\n" \
			"   t           do not show thread break-down\n" \
			"   o           do not include the <other> bucket\n" \
			" -z <rofs>     supply a ROFS symbol file\n" \
			" -o <oby>      supply an OBY file\n" \
			" -x <config>   supply a config file\n" \
			" -h config     display an example of config file\n" \
			<< flush;
	}

void Analyse::ExplainConfigUsage()
	{
	Information();
	cout << "Example of config file:"		<< endl	<< endl;
	cout << "[Common]"								<< endl;
	cout << "TraceFile=PROFILER.DAT"				<< endl;
	cout << "Mode=listing|profile|activity"			<< endl;
	cout << "SymbolFile=core4r.bin.symbol"			<< endl;
	cout << "Range=100-200 | 100+100"				<< endl;
	cout << "IncludeNullThread=0|1"					<< endl;
	cout << "[Profile]"								<< endl;
	cout << "Thread="								<< endl;
	cout << "Dll="									<< endl;
	cout << "Function="								<< endl;
	cout << "Range=1f1a+20 | 1f1a-1f3a"				<< endl;
	cout << "[Partition]"							<< endl;
	cout << "Mode=dll|function"						<< endl;
	cout << "BucketSize="							<< endl;
	cout << "NumberOfBuckets="						<< endl;
	cout << "[Format]"								<< endl;
	cout << "Mode=percentages|samples|excel"		<< endl;
	cout << "ZeroValues=0|1"						<< endl;
	cout << "NoOthers=0|1"							<< endl;
	cout << "TotalOnly=0|1"							<< endl;
	cout << "[NonXIP]"								<< endl;
	cout << "ObyFile1=myrofs.oby"					<< endl;
	cout << "RofsSymbolFile1=rofs.bin.symbol"		<< endl;
	cout << flush;
	}

class Options
	{
	struct Entry
		{
		const char* iName;
		int iOption;
		};
	const static Entry KOptions[];
	static int Compare(const char* aLhs, const char* aRhs);
public:
	static int Get(istrstream& aStr);
	};


const Options::Entry Options::KOptions[] = 
	{
	{"activity",'v'},
	{"address",	'a'},
	{"by",		'b'},
	{"cutoff",	'c'},
	{"dll",		'd'},
	{"excel",	'x'},
	{"format",	'm'},
	{"function",'f'},
	{"help",	'h'},
	{"listing",	'l'},
	{"null",	'n'},
	{"number",	'n'},
	{"other",	'o'},
	{"percent",	'p'},
	{"profile",	'p'},
	{"rom",		'r'},
	{"samples",	's'},
	{"size",	's'},
	{"thread",	't'},
	{"total",	't'},
	{"zero",	'z'},
	{"oby",		'o'},
	{"rofs",	'z'},
	{"config",	'x'},
	};

inline int min(int a, int b)
	{
	return a < b ? a : b;
	}

int Options::Compare(const char* aLhs, const char* aRhs)
	{
	int len = min(strlen(aLhs), strlen(aRhs));
	return strnicmp(aLhs, aRhs, len);
	}

int Options::Get(istrstream& aStr)
	{
	int pos = aStr.tellg();
	const char* s = aStr.str() + pos;

	if (strlen(s) >= 3)
		{
		int l = 0, r = sizeof(KOptions)/sizeof(KOptions[0]);
		do
			{
			int m = (l + r ) >> 1;
			const Entry& e = KOptions[m];
			int k = Compare(s, e.iName);
			if (k < 0)
				r = m;
			else if (k > 0)
				l = m + 1;
			else
				{
				// found a match
				aStr.ignore(strlen(e.iName));
				return e.iOption;
				}
			} while (l < r);
		}
	// no match
	return aStr.get();
	}

int Analyse::ProcessCommandLine(int argc, char ** argv)
	{
	int initial_argc = argc;
	char ** initial_argv = argv;
	// added 2-nd pass. on the 1-st just look for config file
	for(int pass = 0;pass < 2;pass++)
		{
		argc = initial_argc;
		argv = initial_argv;
		while (--argc>0)
			{
			istrstream arg(*++argv);
			int c = arg.get();
			if (c != '/' && c != '-')
				{
				if (pass == 0) continue;
				sTraces.clear();
				sTraces.push_back(arg.str());
				continue;
				}
			c = Options::Get(arg);
			if (tolower(c) != 'x' && pass == 0)
				continue;
			switch (c)
				{
			case 'h': case 'H': case '?':
				if (--argc > 0 && !stricmp(*++argv,"config")) 
					return 2;
				return 1;
			case 'l': case 'L':
				sAction = ETrace;
				break;
			case 'p': case 'P':
				sAction = EProfile;
				break;
			case 'v': case 'V':
				sAction = EActivity;
				break;
			case 'r': case 'R':
				if (--argc == 0)
					Abort("No symbol file specified for option '-r'");
				sRomFile = *++argv;
				break;
			case 's': case 'S':
				sOptions |= ERange;
				arg >> sBeginSample;
				c = arg.get();
				arg >> sEndSample;
				if (c == '+')
					sEndSample += sBeginSample;
				else if (c != '-')
					return 1;
				break;
			case 'n': case 'N':
				sOptions|=ENull;
				break;
			case 't': case 'T':
				if (--argc == 0)
					Abort("No thread name specified for option '-t'");
				sThread = *++argv;
				break;
			case 'd': case 'D':
				if (--argc == 0)
					Abort("No DLL name specified for option '-d'");
				sDll = *++argv;
				break;
			case 'f': case 'F':
				if (--argc == 0)
					Abort("No function name specified for option '-f'");
				sFunction = *++argv;
				break;
			case 'a': case 'A':
				sOptions |= EAddress;
				arg >> hex >> sBase;
				c = arg.get();
				arg >> hex >> sLim;
				if (c == '+')
					sLim += sBase;
				else if (c != '-')
					return 1;
				break;
			case 'b': case 'B':
				switch (c = Options::Get(arg))
					{
				case 'd': case 'D':
					sPartition = EDll;
					break;
				case 'f': case 'F':
					sPartition = EFunction;
					break;
				case 'n': case 'N':
					sPartition = EBuckets;
					arg >> dec >> sBuckets;
					break;
				case 's': case 'S':
					sPartition = ESize;
					arg >> dec >> sBucketSize;
					break;
					}
				break;
			case 'c': case 'C':
				arg >> sCutOff;
				break;
			case 'm': case 'M':
				while ((c = Options::Get(arg)) != EOF)
					{
					switch (c)
						{
					case 'p': case 'P':
						sFormat = EPercent;
						break;
					case 's': case 'S':
						sFormat = ESamples;
						break;
					case 'x': case 'X':
						sFormat = EExcel;
						break;
					case 'z': case 'Z':
						sOptions |= EZeros;
						break;
					case 'o': case 'O':
						sOptions |= ENoOther;
						break;
					case 't': case 'T':
						sOptions |= ETotalOnly;
						break;
					default:
						arg.putback(c);
						break;
						}
					}
				break;
			case 'o': case 'O':
				if (--argc == 0)
					Abort("No OBY file name specified for option '-o'");
				gNonXIP.AddObyFile(*++argv);
				break;
			case 'z': case 'Z':
				if (--argc == 0)
					Abort("No ROFS symbol file name specified for option '-z'");
				gNonXIP.AddSymbolFile(*++argv);
				break;
			case 'x': case 'X':
				if (--argc == 0)
					Abort("No config file name specified for option '-x'");
				if (pass == 0)
					{
					switch(ProcessCfgFile(*++argv))
						{
					case ENoCfgFile:
						Abort("Error no config file name specified for option '-x'");
					case EErrorCfgFile:
						Abort("Error in config file");
						}
					}
				else
					++argv;
				break;
			default:			// unrecognised option
				arg.putback(c);
				break;
				}
			if (!arg || arg.get() != EOF)
				{
				cerr << "Unrecognised option \'" << arg.str() << '\'' << endl;
				Abort();
				}
			} // while
		} // for(pass)
	if (sTraces.empty())
		Abort("No trace files specified");
	return sTraces.size() != 1;
	}

CodeSpace* Analyse::CreateCodeSpace(SymbolFile* aSymbols, NonXIP *aNonXIP)
	{
	if (Option(EAddress))
		{
		unsigned size;
		if (Partition() == ESize)
			size = sBucketSize;
		else
			size = (sLim - sBase) / sBuckets;
		return new AddressCodeSpace(sBase, sLim, size, AddressCodeSpace::EAbsolute);
		}

	MappedCodeSpace * mapped_code_space = 0;
	if (aSymbols == 0)
		{
		MappedCodeSpace* mapped_code_space =  new MappedCodeSpace();
		if (aNonXIP) 
			aNonXIP->SetMappedCodeSpace(mapped_code_space);
		return mapped_code_space;
		}

	for (;;)
		{
		switch (Partition())
			{
		case EDefault:
			if (sFunction != 0)
				{
				sPartition = ESize;
				sBucketSize = 4;
				}
			else if (sDll != 0)
				sPartition = EFunction;
			else
				sPartition = EDll;
			break;
		case EDll:
			{
			PartitionByDll p(sDll);
			mapped_code_space =  new MappedCodeSpace(*aSymbols,p);
			if (aNonXIP) 
				aNonXIP->SetMappedCodeSpace(mapped_code_space);
			return mapped_code_space;
			}
		case EFunction:
			{
			PartitionByFunction p(sDll, sFunction);
			mapped_code_space =  new MappedCodeSpace(*aSymbols,p);
			if (aNonXIP) 
				aNonXIP->SetMappedCodeSpace(mapped_code_space);
			return mapped_code_space;
			}
		case ESize:
		case EBuckets:
			if (sFunction == 0)
				sPartition = EFunction;
			else
				{
				FindFunction f(sDll, sFunction);
				aSymbols->Parse(f);
				if (f.iPc == 0)
					{
					cerr << "Cannot find function '" << sFunction << '\'';
					if (sDll)
						cerr << " in '" << sDll << '\'';
					cerr << endl;
					Abort();
					}
				unsigned size = (Partition() == ESize) ? sBucketSize : f.iLength / sBuckets;
				return new AddressCodeSpace(f.iPc, f.iPc + f.iLength, size, AddressCodeSpace::ERelative);
				}
			break;
			}
		}
	}

Sampler* Analyse::CreateSampler(SymbolFile* aSymbols, NonXIP *aNonXIP)
	{
	switch (Action())
		{
	case ETrace:
		{
		MappedCodeSpace * mapped_code_space = 0;
		if (aSymbols == 0)
			//return new Tracer(0);
			mapped_code_space = new MappedCodeSpace();
		else
			{
			PartitionByFunction p(0, 0);
			mapped_code_space = new MappedCodeSpace(*aSymbols,p);
			}
		if (aNonXIP) aNonXIP->SetMappedCodeSpace(mapped_code_space);
		return new Tracer(mapped_code_space);
		}
	case EProfile:
		{
		CodeSpace * code_space = CreateCodeSpace(aSymbols, aNonXIP);
		return new Distribution(*code_space, sCutOff);
		}
	case EActivity:
		return new Activity(Partition() == ESize ? sBucketSize : 100, sBeginSample, sCutOff);
		}
	return 0;
	}

void Analyse::Run()
//
// The main part of the program
//
	{
	Information();
	Trace trace;
	trace.Load(sTraces[0], sBeginSample, sEndSample);
	// create map of original/segment names
	gNonXIP.CreateNamesMap();

	SymbolFile* symbols = 0;
	if (sRomFile)
		symbols = new SymbolFile(sRomFile);
	Sampler* sampler = CreateSampler(symbols, &gNonXIP);
	trace.Decode(*sampler, &gNonXIP);
	
	// NonXIP footer messages
	cout << endl << "Row buffer errors:" << gNonXIP.iRowBufferErrors;
	cout << " Cook buffer errors:" << gNonXIP.iCookBufferErrors;
	cout << "  Mode:";
	if (gNonXIP.iReportMask & NonXIP::ENonXip)
		cout << "NonXIP";
	else
		cout << "XIP only";
	if (gNonXIP.iReportMask & NonXIP::ENodebugSupport)
		cout << " No debug support from Kernel";

	cout << endl;

	cout << flush;
	}

bool Analyse::Match(const char* aString, const char* aMatch)
//
// Wildcard matching
// If match string is 0, then matches everything
//
	{
	if (aMatch == 0)
		return true;

	const char* star = strchr(aMatch, '*');
	if (star == 0)
		return (stricmp(aString, aMatch) == 0);

	int mlen = star - aMatch;
	if (strnicmp(aString, aMatch, mlen) != 0)
		return false;

	const char* end = aString + strlen(aString);

	for (;;)
		{
		aString += mlen;
		aMatch += mlen + 1;
		star = strchr(aMatch, '*');
		if (star == 0)
			return (stricmp(end - strlen(aMatch), aMatch) == 0);
		mlen = star - aMatch;
		const char* lim = end - mlen;
		for (;;)
			{
			if (aString > lim)
				return false;
			if (strnicmp(aString, aMatch, mlen) == 0)
				break;
			++aString;
			}
		}
	}

void Analyse::Abort(char const* aMessage)
	{
	cerr << aMessage << endl;
	Abort();
	}

void Analyse::Abort()
	{
	exit(3);
	}

void Analyse::Corrupt(char const* aMessage)
//
// terminate after detecting a fatal corruption error
//
	{
	cerr << "\nfatal error: " << aMessage << "\ncannot continue\n" << flush;
	exit(2);
	}

ostream& Analyse::Error()
	{
	return cerr << "error: ";
	}

ostream& Analyse::Warning()
	{
	return cerr << "warning: ";
	}