diff -r 000000000000 -r a41df078684a kerneltest/e32utils/analyse/analyse.cpp --- /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 +#include +#else //!__MSVCDOTNET__ +#include +#include +#endif //__MSVCDOTNET__ + +#include + +Analyse::TAction Analyse::sAction; +Analyse::TFormat Analyse::sFormat; +Analyse::TPartition Analyse::sPartition; +int Analyse::sOptions; +std::vector 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 supply a Rom symbol file\n" \ + " -s restrict the profile to the samples specified\n" \ + " This is specified either as - or\n" \ + " as + in decimal\n" \ + " -n include NULL thread\n" \ + " -t profile threads matching the pattern\n" \ + " -d profile DLL (or EXE) matching the pattern\n" \ + " -f profile the function matching the pattern\n" \ + " -a profile the address range specified\n" \ + " This is specified either as - or\n" \ + " as + in hexadecimal\n" \ + " -bd partition the profile by dll/exe\n" \ + " -bf partition the profile by function\n" \ + " -bs partition the profile into buckets of size n\n" \ + " -bn partition the profile into approx. n buckets\n" \ + " -c 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 bucket\n" \ + " -z supply a ROFS symbol file\n" \ + " -o supply an OBY file\n" \ + " -x 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: "; + } +