408 lines
13 KiB
C++
408 lines
13 KiB
C++
//
|
|
// SuperTuxKart - a fun racing game with go-kart
|
|
// Copyright (C) 2014-2015 Joerg Henrichs
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#ifdef __MINGW32__
|
|
#undef _WIN32_WINNT
|
|
#define _WIN32_WINNT 0x0500
|
|
#endif
|
|
|
|
#include "config/hardware_stats.hpp"
|
|
|
|
#include "config/user_config.hpp"
|
|
#include "graphics/central_settings.hpp"
|
|
#include "graphics/glwrap.hpp"
|
|
#include "graphics/irr_driver.hpp"
|
|
#include "online/http_request.hpp"
|
|
#include "utils/random_generator.hpp"
|
|
|
|
#ifdef __APPLE__
|
|
# include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#ifndef WIN32
|
|
# include <sys/param.h> // To get BSD macro
|
|
# include <sys/utsname.h>
|
|
#endif
|
|
#include <vector>
|
|
|
|
|
|
namespace HardwareStats
|
|
{
|
|
|
|
namespace Private
|
|
{
|
|
/** Stores the OS version, e.g. "Windows 7", or "Fedora 21". */
|
|
static std::string m_os_version;
|
|
} // namespace Private
|
|
using namespace Private;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Returns the amount of RAM in MB.
|
|
* (C) 2014-2015 Wildfire Games (0 A.D.), ported by Joerg Henrichs
|
|
*/
|
|
int getRAM()
|
|
{
|
|
#ifdef __linux__
|
|
const uint64_t memory_size = (uint64_t)sysconf(_SC_PHYS_PAGES)
|
|
* sysconf(_SC_PAGESIZE);
|
|
return int(memory_size / (1024*1024));
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
MEMORYSTATUSEX mse;
|
|
mse.dwLength = sizeof(mse);
|
|
const bool ok = GlobalMemoryStatusEx(&mse)==TRUE;
|
|
|
|
DWORDLONG memory_size = mse.ullTotalPhys;
|
|
// Richter, "Programming Applications for Windows": the reported
|
|
// value doesn't include non-paged pool reserved during boot;
|
|
// it's not considered available to the kernel. (the amount is
|
|
// 528 KiB on a 512 MiB WinXP/Win2k machine). we'll round up
|
|
// to the nearest megabyte to fix this.
|
|
const DWORDLONG mbyte = 1024*1024;
|
|
return (int)ceil(memory_size/mbyte);
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
size_t memory_size = 0;
|
|
size_t len = sizeof(memory_size);
|
|
// Argh, the API doesn't seem to be const-correct
|
|
/*const*/ int mib[2] = { CTL_HW, HW_PHYSMEM };
|
|
sysctl(mib, 2, &memory_size, &len, 0, 0);
|
|
memory_size /= (1024*1024);
|
|
return int(memory_size);
|
|
#endif
|
|
Log::error("HW report",
|
|
"No RAM information available for hardware report.");
|
|
return 0;
|
|
} // getRAM
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Returns the number of processors on the system.
|
|
* (C) 2014-2015 Wildfire Games (0 A.D.), ported by Joerg Henrichs
|
|
*/
|
|
int getNumProcessors()
|
|
{
|
|
#if defined(__linux__) || defined(__CYGWIN__)
|
|
return sysconf(_SC_NPROCESSORS_CONF);
|
|
#endif
|
|
#ifdef WIN32
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si); // guaranteed to succeed
|
|
return si.dwNumberOfProcessors;
|
|
#endif
|
|
#ifdef __APPLE__
|
|
// Mac OS X doesn't have sysconf(_SC_NPROCESSORS_CONF)
|
|
int mib[] = { CTL_HW, HW_NCPU };
|
|
int ncpus;
|
|
size_t len = sizeof(ncpus);
|
|
int ret = sysctl(mib, 2, &ncpus, &len, NULL, 0);
|
|
assert(ret != -1);
|
|
return ncpus;
|
|
#endif
|
|
Log::error("HW report",
|
|
"Number of processors not available for hardware report.");
|
|
return 0;
|
|
} // getNumProcessors
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Tries opening and parsing the specified release file in /etc to find
|
|
* information about the distro used.
|
|
* \param filename Full path of the file to open.
|
|
* \return True if file could be read and valid information was paresed,
|
|
* false otherwise.
|
|
*/
|
|
bool readEtcReleaseFile(const std::string &filename)
|
|
{
|
|
std::ifstream in(filename);
|
|
std::string s, distro, version;
|
|
while( (distro.empty() || version.empty()) &&
|
|
std::getline(in, s) )
|
|
{
|
|
std::vector<std::string> l = StringUtils::split(s, '=');
|
|
if(l.size()==0) continue;
|
|
if (l[0]=="NAME" ) distro = l[1];
|
|
else if(l[0]=="VERSION_ID") version = l[1];
|
|
}
|
|
if(!distro.empty() && !version.empty())
|
|
{
|
|
distro = StringUtils::replace(distro, "\"", "");
|
|
version = StringUtils::replace(version, "\"", "");
|
|
m_os_version = distro + " " + version;
|
|
return true;
|
|
}
|
|
return false;
|
|
} // readEtcReleaseFile
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Identify more details about the OS, e.g. on linux which distro
|
|
* and which verison; on windows the version number.
|
|
* \param json Json data structure to store the os info in.
|
|
*/
|
|
void determineOSVersion()
|
|
{
|
|
std::string version, distro;
|
|
|
|
#ifdef __linux__
|
|
// First try the standard /etc/os-release. Then check for older versions
|
|
// e.g. /etc/fedora-release, /etc/SuSE-release, /etc/redhat-release
|
|
if(readEtcReleaseFile("/etc/os-release")) return;
|
|
|
|
std::set<std::string> file_list;
|
|
file_manager->listFiles(file_list, "./", true);
|
|
for(std::set<std::string>::iterator i = file_list.begin();
|
|
i != file_list.end(); i++)
|
|
{
|
|
// Only try reading /etc/*-release files
|
|
if(StringUtils::hasSuffix(*i, "-release"))
|
|
if (readEtcReleaseFile(*i)) return;
|
|
}
|
|
// Fallback in case that we can't find any valid information in /etc/*release
|
|
struct utsname u;
|
|
if (uname(&u))
|
|
{
|
|
m_os_version = "Linux unknown";
|
|
return;
|
|
}
|
|
// Ignore data after "-", since it could identify a system (self compiled
|
|
// kernels).
|
|
std::vector<std::string> l = StringUtils::split(std::string(u.release),'-');
|
|
m_os_version = std::string(u.sysname) + " " + l[0];
|
|
|
|
#endif
|
|
|
|
#ifdef BSD
|
|
struct utsname u;
|
|
if (uname(&u))
|
|
{
|
|
m_os_version = "BSD unknown";
|
|
return;
|
|
}
|
|
// Ignore data after "-", since it could identify a system (self compiled
|
|
// kernels).
|
|
std::vector<std::string> l = StringUtils::split(std::string(u.release),'-');
|
|
m_os_version = std::string(u.sysname) + " " + l[0];
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
// (C) 2014-2015 Wildfire Games (0 A.D.), ported by Joerg Henrichs.
|
|
|
|
HKEY hKey;
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0,
|
|
KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
|
|
{
|
|
m_os_version = "windows-unknown";
|
|
return;
|
|
}
|
|
char windows_version_string[20];
|
|
DWORD size = sizeof(windows_version_string);
|
|
RegQueryValueEx(hKey, "CurrentVersion", 0, 0, (LPBYTE)windows_version_string, &size);
|
|
unsigned major = 0, minor = 0;
|
|
|
|
std::stringstream sstr(windows_version_string);
|
|
sstr >> major;
|
|
if (sstr.peek() == '.')
|
|
sstr.ignore();
|
|
sstr >> minor;
|
|
|
|
int windows_version = (major << 8) | minor;
|
|
RegCloseKey(hKey);
|
|
|
|
switch(windows_version)
|
|
{
|
|
case 0x0500: m_os_version="Windows 2000"; break;
|
|
case 0x0501: m_os_version="Windows XP"; break;
|
|
case 0x0502: m_os_version="Windows XP64"; break;
|
|
case 0x0600: m_os_version="Windows Vista"; break;
|
|
case 0x0601: m_os_version="Windows 7"; break;
|
|
case 0x0602: m_os_version="Windows 8"; break;
|
|
case 0x0603: m_os_version="Windows 8_1"; break;
|
|
default: {
|
|
m_os_version = StringUtils::insertValues("Windows %d",
|
|
windows_version);
|
|
break;
|
|
}
|
|
} // switch
|
|
|
|
#endif
|
|
} // determineOSVersion
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Returns the OS version, e.g.: "Windows 7", or "Fedora 21".
|
|
*/
|
|
const std::string& getOSVersion()
|
|
{
|
|
if(m_os_version.empty())
|
|
determineOSVersion();
|
|
return m_os_version;
|
|
} // getOSVersion
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** If the configuration of this installation has not been reported for the
|
|
* current version, collect the hardware statistics and send it to STK's
|
|
* server.
|
|
*/
|
|
void reportHardwareStats()
|
|
{
|
|
#ifdef SERVER_ONLY
|
|
return;
|
|
#else
|
|
if(!UserConfigParams::m_hw_report_enable)
|
|
return;
|
|
|
|
// Version of the hw report, which is stored in the DB. If new fields
|
|
// are added, increase this version. Each STK installation will report
|
|
// its configuration only once (per version number). So if the version
|
|
// number is increased, a new report will be sent.
|
|
const int report_version = 1;
|
|
if(UserConfigParams::m_last_hw_report_version>=report_version) return;
|
|
while(UserConfigParams::m_random_identifier==0)
|
|
{
|
|
RandomGenerator rg;
|
|
UserConfigParams::m_random_identifier = rg.get(1<<30);
|
|
user_config->saveConfig();
|
|
}
|
|
|
|
Json json;
|
|
#ifdef WIN32
|
|
json.add("os_win", 1);
|
|
#else
|
|
json.add("os_win", 0);
|
|
#endif
|
|
#ifdef __APPLE__
|
|
json.add("os_macosx", 1);
|
|
#else
|
|
json.add("os_macosx", 0);
|
|
#endif
|
|
#ifdef ANDROID
|
|
json.add("os_android", 1);
|
|
#else
|
|
json.add("os_android", 0);
|
|
#endif
|
|
#if defined(__linux__) && !defined(ANDROID)
|
|
json.add("os_linux", 1);
|
|
json.add("os_unix", 1);
|
|
#else
|
|
json.add("os_linux", 0);
|
|
json.add("os_unix", 0);
|
|
#endif
|
|
#ifdef DEBUG
|
|
json.add("build_debug", 1);
|
|
#endif
|
|
|
|
json.add("os_version", getOSVersion());
|
|
|
|
unsigned int ogl_version = CVS->getGLSLVersion();
|
|
unsigned int major = ogl_version/100;
|
|
unsigned int minor = ogl_version - 100*major;
|
|
std::string version =
|
|
StringUtils::insertValues("%d.%d", major, minor);
|
|
json.add("GL_SHADING_LANGUAGE_VERSION", version);
|
|
|
|
std::string vendor, renderer, full_version;
|
|
irr_driver->getOpenGLData(&vendor, &renderer, &full_version);
|
|
json.add("gfx_drv_ver", "OpenGL "+vendor);
|
|
|
|
std::string card_name = vendor;
|
|
if(StringUtils::startsWith(card_name, "ATI Technologies Inc."))
|
|
card_name="ATI";
|
|
else if (StringUtils::startsWith(card_name, "NVIDIA Corporation"))
|
|
card_name="NVIDIA";
|
|
else if(StringUtils::startsWith(card_name, "S3 Graphics"))
|
|
card_name="S3";
|
|
json.add("gfx_card", card_name+" "+renderer);
|
|
|
|
json.add("video_xres", UserConfigParams::m_width );
|
|
json.add("video_yres", UserConfigParams::m_height);
|
|
|
|
int mem = getRAM();
|
|
if(mem>0)
|
|
json.add("ram_total", mem);
|
|
|
|
int nr_procs = getNumProcessors();
|
|
if(nr_procs>0)
|
|
json.add("cpu_numprocs", nr_procs);
|
|
|
|
#ifndef SERVER_ONLY
|
|
json.add("GL_EXTENSIONS", getGLExtensions());
|
|
getGLLimits(&json);
|
|
#endif
|
|
json.finish();
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** A small class which sends the HW report to the STK server. On
|
|
* completion, it will either update the last-submitted-hw-report version,
|
|
* or log an error message (in which case next time STK is started it
|
|
* wil try again to log the report).
|
|
*/
|
|
class HWReportRequest : public Online::HTTPRequest
|
|
{
|
|
private:
|
|
/** Version number of the hw report. */
|
|
int m_version;
|
|
public:
|
|
HWReportRequest(int version) : Online::HTTPRequest(/*manage memory*/true, 1)
|
|
, m_version(version)
|
|
{}
|
|
// --------------------------------------------------------------------
|
|
/** Callback after the request has been executed.
|
|
*/
|
|
virtual void callback()
|
|
{
|
|
// If the request contains incorrect data, it will not have a
|
|
// download error, but return an error string as return value:
|
|
if(hadDownloadError() || getData()=="<h1>Bad Request (400)</h1>")
|
|
{
|
|
Log::error("HW report", "Error uploading the HW report.");
|
|
if(hadDownloadError())
|
|
Log::error("HW report", "%s", getDownloadErrorMessage());
|
|
else
|
|
Log::error("HW report", "%s", getData().c_str());
|
|
}
|
|
else
|
|
{
|
|
Log::info("HW report", "Upload successful.");
|
|
UserConfigParams::m_last_hw_report_version = m_version;
|
|
// The callback is executed by the main thread, so no need
|
|
// to worry about locks when writing the file.
|
|
user_config->saveConfig();
|
|
}
|
|
} // callback
|
|
|
|
}; // HWReportRequest
|
|
// ------------------------------------------------------------------------
|
|
|
|
Online::HTTPRequest *request = new HWReportRequest(report_version);
|
|
request->addParameter("user_id", UserConfigParams::m_random_identifier);
|
|
request->addParameter("time", StkTime::getTimeSinceEpoch());
|
|
request->addParameter("type", "hwdetect");
|
|
request->addParameter("version", report_version);
|
|
request->addParameter("data", json.toString());
|
|
request->setURL((std::string)UserConfigParams::m_server_hw_report+"/upload/v1/");
|
|
//request->setURL("http://127.0.0.1:8000/upload/v1/");
|
|
request->queue();
|
|
#endif // !SERVER_ONLY
|
|
} // reportHardwareStats
|
|
|
|
} // namespace HardwareStats
|