// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "nel/misc/file.h"
#include "nel/misc/path.h"
#include "nel/misc/vector.h"
#include "nel/misc/algo.h"
using namespace std;
using namespace NLMISC;
#define myinfo NLMISC::createDebug (), NLMISC::InfoLog->setPosition( __LINE__, __FILE__ ), NLMISC::InfoLog->displayRawNL
// ***************************************************************************
void filterRyzomBug(const char *dirSrc, const char *dirDst, uint patchVersionWanted, const string specialFilter)
{
if(!CFile::isDirectory(dirDst))
{
if(!CFile::createDirectory(dirDst))
{
myinfo("%s is not a directory and cannot create it", dirDst);
return;
}
}
vector fileList;
CPath::getPathContent(dirSrc, false, false, true, fileList, NULL, true);
for(uint i=0;i dmpList;
dmpList.clear();
CPath::getPathContent(dmpDirSrc, false, false, true, dmpList, NULL);
for(uint j=0;j TStatMap;
typedef map TStatStrMap;
typedef map TStatStrStrMap;
class CCrashCont
{
public:
string Name;
sint X0,Y0,X1,Y1;
uint NumCrash;
CCrashCont(const std::string &name, sint x0, sint y0, sint x1, sint y1)
{
Name= name;
X0= min(x0,x1);
Y0= min(y0,y1);
X1= max(x0,x1);
Y1= max(y0,y1);
NumCrash= 0;
}
bool testPos(const CVector &pos)
{
if(pos.x>=X0 && pos.x<=X1 && pos.y>=Y0 && pos.y<=Y1)
{
NumCrash++;
return true;
}
return false;
}
};
void statRyzomBug(const char *dirSrc)
{
vector fileList;
CPath::getPathContent(dirSrc, false, false, true, fileList, NULL, true);
// delete the log.log
CFile::deleteFile("log.log");
TStatStrMap senderMap;
TStatMap shardMap;
TStatMap timeInGameMap;
TStatMap patchVersionMap;
TStatMap info3dMap;
std::vector userPosArray;
TStatStrStrMap senderToCrashFileMap;
TStatStrStrMap crashFileToSenderMap;
uint totalCrash= 0;
uint totalCrashDuplicate= 0;
// **** parse all files
for(uint i=0;isint64(60) )
{
senderMap[precSenderId].Val++;
shardMap[precShardId].Val++;
timeInGameMap[precTimeInGame].Val++;
patchVersionMap[precPatchVersion].Val++;
if(precNel3DMode!=0 && precNel3DMode!=1)
precNel3DMode= 2;
if(precCard3D!=0 && precCard3D!=1)
precCard3D= 2;
info3dMap[precNel3DMode*256+precCard3D].Val++;
userPosArray.push_back(precUserPos);
crashFileToSenderMap[fileNoDir][precSenderId].Val++;
senderToCrashFileMap[precSenderId][fileNoDir].Val++;
totalCrash++;
}
totalCrashDuplicate++;
}
}
}
}
// **** display Stats
// general stats
myinfo("**** Total: %d Crashs (%d with duplicates)", totalCrash, totalCrashDuplicate);
myinfo("NB: 'duplicates' means: crashs that are removed because suppose the player click 'ignore' (same sender/same Localtime, within about 1 min)");
// senderId
TStatMap::iterator it;
TStatStrMap::iterator itStr;
myinfo("");
myinfo("**** Stat Per Sender:");
multimap resortSender;
for(itStr=senderMap.begin();itStr!=senderMap.end();itStr++)
{
resortSender.insert(make_pair(itStr->second.Val, itStr->first));
}
multimap::iterator it2;
for(it2=resortSender.begin();it2!=resortSender.end();it2++)
{
myinfo("**** %d Crashs for UserId %s", it2->first, it2->second.c_str());
}
// shardId
myinfo("");
myinfo("**** Stat Per ShardId:");
for(it=shardMap.begin();it!=shardMap.end();it++)
{
myinfo("**** %d Crashs for ShardId %d", it->second.Val, it->first);
}
// timeInGame
myinfo("");
myinfo("**** Stat Per TimeInGame:");
for(it=timeInGameMap.begin();it!=timeInGameMap.end();it++)
{
myinfo("**** %d Crashs for TimeInGame %s", it->second.Val, it->first==0?"0h 0min 0sec":
(it->first==1?"<=5 min":
it->first==2?"> 5 min":"??? Bad parse ???"));
}
// infoPatch
myinfo("");
myinfo("**** Stat Per PatchVersion:");
for(it=patchVersionMap.begin();it!=patchVersionMap.end();it++)
{
myinfo("**** %d Crashs for PatchVersion %d", it->second.Val, it->first);
}
// info3d
myinfo("");
myinfo("**** Stat Per 3d Mode:");
for(it=info3dMap.begin();it!=info3dMap.end();it++)
{
uint card3d= it->first&255;
uint mode3d= it->first>>8;
myinfo("**** %d Crashs for %s / Card %s", it->second.Val,
mode3d==0?"OpenGL":(mode3d==1?"Direct3D":"??? No Driver ???"),
card3d==0?"NVIDIA":(card3d==1?"RADEON":"Misc"));
}
// crash by continent
{
// init cont info
CCrashCont crashCont[]=
{
// New First, because bbox may be included in Main bbox
CCrashCont("Matis Newb", 0,-5000,2800,-8500),
CCrashCont("Zorai Newb", 6500,-4000,9300,-6000),
CCrashCont("Trykr Newb", 20000,-32000,24000,-36000),
CCrashCont("Fyros Newb", 20500,-24500,24000,-27500),
CCrashCont("Matis Main", 0,0,6500,-8500),
CCrashCont("Zorai Main", 6500,0,13000,-6000),
CCrashCont("Trykr Main ", 13000,-29000,20000,-36000),
CCrashCont("Fyros Main ", 15000,-23000,20500,-27500)
};
uint numCont= sizeof(crashCont)/sizeof(crashCont[0]);
uint numNotFound= 0;
// count stats
uint i;
for(i=0;isecond;
TStatStrMap &crashFileMap= senderToCrashFileMap[userId];
if(crashFileMap.empty())
{
myinfo(" Error parsing Crashs for UserId %s (?????)", userId.c_str());
}
else
{
myinfo(" %d Crashs for %s:", it2->first, userId.c_str());
for(TStatStrMap::iterator it=crashFileMap.begin();it!=crashFileMap.end();it++)
{
myinfo(" %d in %s", it->second.Val, it->first.c_str());
}
}
}
// Stats per Crash File
myinfo("");
myinfo("**** Detailed Crashs per crash Log:");
multimap resortCrashLog;
for(TStatStrStrMap::iterator itStrStr=crashFileToSenderMap.begin();itStrStr!=crashFileToSenderMap.end();itStrStr++)
{
// count total crash instance
uint numCrash= 0;
TStatStrMap &userIdMap= itStrStr->second;
for(TStatStrMap::iterator it=userIdMap.begin();it!=userIdMap.end();it++)
numCrash+= it->second.Val;
// insert for resort by this number
resortCrashLog.insert(make_pair(numCrash, itStrStr->first));
}
for(it2=resortCrashLog.begin();it2!=resortCrashLog.end();it2++)
{
string crashLog= it2->second;
TStatStrMap &userIdMap= crashFileToSenderMap[crashLog];
if(userIdMap.empty())
{
myinfo(" Error parsing Crashs for CrashFile %s (?????)", crashLog.c_str());
}
else
{
myinfo(" %d Crashs in %s:", it2->first, crashLog.c_str());
for(TStatStrMap::iterator it=userIdMap.begin();it!=userIdMap.end();it++)
{
myinfo(" %d for %s", it->second.Val, it->first.c_str());
}
}
}
// RAW userPos
myinfo("");
myinfo("**** RAW Crashs Pos (copy in excel, and use insert/chart/(X/Y)Scatter):");
for(uint i=0;i= 5 && argv[2]==string("-p"))
ok= true,filterMode= true;
if(!ok)
{
myinfo("Usage1 (stats):\n\t%s src_dir -s\n", CFile::getFilename(argv[0]).c_str());
myinfo("Usage2 (patch filter):\n\t%s src_dir -p patch_version dst_dir [specialFilter]\n", CFile::getFilename(argv[0]).c_str());
}
else
{
if(!CFile::isDirectory(argv[1]))
{
myinfo("%s is not a directory", argv[1]);
return 1;
}
if(filterMode)
{
string specialFilter;
if(argc>=6)
specialFilter= argv[5];
filterRyzomBug(argv[1], argv[4], atoi(argv[3]), specialFilter);
}
else if(statMode)
{
statRyzomBug(argv[1]);
}
}
return 0;
}