kerneltest/e32utils/analyse/analyse.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32utils/analyse/analyse.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,698 @@
+// 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: ";
+	}
+