// ********************************************************************** // // Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) # include // For _getcwd # include // For PDH_MORE_DATA #else # include # if defined(__APPLE__) # include # elif defined(__sun) # include # elif defined(__hpux) # include # elif defined(_AIX) # include # include # endif #endif using namespace std; using namespace IceGrid; namespace { #ifdef _WIN32 string pdhErrorToString(PDH_STATUS err) { return IceUtilInternal::errorToString(err, GetModuleHandle(TEXT("PDH.DLL"))); } static string getLocalizedPerfName(int idx, const Ice::LoggerPtr& logger) { vector localized; unsigned long size = 256; localized.resize(size); PDH_STATUS err; while((err = PdhLookupPerfNameByIndex(0, idx, &localized[0], &size)) == PDH_MORE_DATA) { size += 256; localized.resize(size); } if(err != ERROR_SUCCESS) { Ice::Warning out(logger); out << "Unable to lookup the performance counter name:\n"; out << pdhErrorToString(err); out << "\nThis usually occurs when you do not have sufficient privileges"; throw Ice::SyscallException(__FILE__, __LINE__, err); } return string(&localized[0]); } class UpdateUtilizationAverageThread : public IceUtil::Thread { public: UpdateUtilizationAverageThread(PlatformInfo& platform) : IceUtil::Thread("IceGrid update utilization average thread"), _platform(platform) { } virtual void run() { _platform.runUpdateLoadInfo(); } private: PlatformInfo& _platform; }; #endif } namespace IceGrid { RegistryInfo toRegistryInfo(const InternalReplicaInfoPtr& replica) { RegistryInfo info; info.name = replica->name; info.hostname = replica->hostname; return info; } NodeInfo toNodeInfo(const InternalNodeInfoPtr& node) { NodeInfo info; info.name = node->name; info.os = node->os; info.hostname = node->hostname; info.release = node->release; info.version = node->version; info.machine = node->machine; info.nProcessors = node->nProcessors; info.dataDir = node->dataDir; return info; } } PlatformInfo::PlatformInfo(const string& prefix, const Ice::CommunicatorPtr& communicator, const TraceLevelsPtr& traceLevels) : _traceLevels(traceLevels) { // // Initialization of the necessary data structures to get the load average. // #if defined(_WIN32) _terminated = false; _usages1.insert(_usages1.end(), 1 * 60 / 5, 0); // 1 sample every 5 seconds during 1 minutes. _usages5.insert(_usages5.end(), 5 * 60 / 5, 0); // 1 sample every 5 seconds during 5 minutes. _usages15.insert(_usages15.end(), 15 * 60 / 5, 0); // 1 sample every 5 seconds during 15 minutes. _last1Total = 0; _last5Total = 0; _last15Total = 0; #elif defined(_AIX) struct nlist nl; nl.n_name = "avenrun"; nl.n_value = 0; if(knlist(&nl, 1, sizeof(nl)) == 0) { _kmem = open("/dev/kmem", O_RDONLY); // // Give up root permissions to minimize security risks, it's // only needed to access /dev/kmem. // setuid(getuid()); setgid(getgid()); } else { _kmem = -1; } #endif // // Get the number of processors. // #if defined(_WIN32) SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); _nProcessors = sysInfo.dwNumberOfProcessors; #elif defined(__APPLE__) static int ncpu[2] = { CTL_HW, HW_NCPU }; size_t sz = sizeof(_nProcessors); if(sysctl(ncpu, 2, &_nProcessors, &sz, 0, 0) == -1) { Ice::SyscallException ex(__FILE__, __LINE__); ex.error = getSystemErrno(); throw ex; } #elif defined(__hpux) struct pst_dynamic dynInfo; if(pstat_getdynamic(&dynInfo, sizeof(dynInfo), 1, 0) >= 0) { _nProcessors = dynInfo.psd_proc_cnt; } else { _nProcessors = 1; } #else _nProcessors = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); #endif // // Get the rest of the node information. // #ifdef _WIN32 _os = "Windows"; char hostname[MAX_COMPUTERNAME_LENGTH + 1]; unsigned long size = sizeof(hostname); if(GetComputerName(hostname, &size)) { _hostname = hostname; } OSVERSIONINFO osInfo; osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osInfo); ostringstream os; os << osInfo.dwMajorVersion << "." << osInfo.dwMinorVersion; _release = os.str(); _version = osInfo.szCSDVersion; switch(sysInfo.wProcessorArchitecture) { #if defined(_MSC_VER) && (_MSC_VER < 1300) case 9: // PROCESSOR_ARCHITECTURE_AMD64 #else case PROCESSOR_ARCHITECTURE_AMD64: #endif _machine = "x64"; break; case PROCESSOR_ARCHITECTURE_IA64: _machine = "IA64"; break; case PROCESSOR_ARCHITECTURE_INTEL: _machine = "x86"; break; default: _machine = "unknown"; break; }; #else struct utsname utsinfo; uname(&utsinfo); _os = utsinfo.sysname; _hostname = utsinfo.nodename; _release = utsinfo.release; _version = utsinfo.version; _machine = utsinfo.machine; #endif Ice::PropertiesPtr properties = communicator->getProperties(); string endpointsPrefix; if(prefix == "IceGrid.Registry") { _name = properties->getPropertyWithDefault("IceGrid.Registry.ReplicaName", "Master"); endpointsPrefix = prefix + ".Client"; } else { _name = properties->getProperty(prefix + ".Name"); endpointsPrefix = prefix; } Ice::PropertyDict props = properties->getPropertiesForPrefix(endpointsPrefix); Ice::PropertyDict::const_iterator p = props.find(endpointsPrefix + ".PublishedEndpoints"); if(p != props.end()) { _endpoints = p->second; } else { _endpoints = properties->getProperty(endpointsPrefix + ".Endpoints"); } #ifdef _WIN32 char cwd[_MAX_PATH]; if(_getcwd(cwd, _MAX_PATH) == NULL) #else char cwd[PATH_MAX]; if(getcwd(cwd, PATH_MAX) == NULL) #endif { throw "cannot get the current directory:\n" + IceUtilInternal::lastErrorToString(); } _cwd = string(cwd); _dataDir = properties->getProperty(prefix + ".Data"); if(!IceUtilInternal::isAbsolutePath(_dataDir)) { _dataDir = _cwd + '/' + _dataDir; } if(_dataDir[_dataDir.length() - 1] == '/') { _dataDir = _dataDir.substr(0, _dataDir.length() - 1); } } PlatformInfo::~PlatformInfo() { #if defined(_AIX) if(_kmem > 0) { close(_kmem); } #endif } void PlatformInfo::start() { #if defined(_WIN32) _updateUtilizationThread = new UpdateUtilizationAverageThread(*this); _updateUtilizationThread->start(); #endif } void PlatformInfo::stop() { #if defined(_WIN32) { IceUtil::Monitor::Lock sync(_utilizationMonitor); _terminated = true; _utilizationMonitor.notify(); } assert(_updateUtilizationThread); _updateUtilizationThread->getThreadControl().join(); _updateUtilizationThread = 0; #endif } NodeInfo PlatformInfo::getNodeInfo() const { return toNodeInfo(getInternalNodeInfo()); } RegistryInfo PlatformInfo::getRegistryInfo() const { return toRegistryInfo(getInternalReplicaInfo()); } InternalNodeInfoPtr PlatformInfo::getInternalNodeInfo() const { InternalNodeInfoPtr info = new InternalNodeInfo(); info->name = _name; info->os = _os; info->hostname = _hostname; info->release = _release; info->version = _version; info->machine = _machine; info->nProcessors = _nProcessors; info->dataDir = _dataDir; return info; } InternalReplicaInfoPtr PlatformInfo::getInternalReplicaInfo() const { InternalReplicaInfoPtr info = new InternalReplicaInfo(); info->name = _name; info->hostname = _hostname; return info; } LoadInfo PlatformInfo::getLoadInfo() { LoadInfo info; info.avg1 = -1.0f; info.avg5 = -1.0f; info.avg15 = -1.0f; #if defined(_WIN32) IceUtil::Monitor::Lock sync(_utilizationMonitor); info.avg1 = static_cast(_last1Total) / _usages1.size() / 100.0f; info.avg5 = static_cast(_last5Total) / _usages5.size() / 100.0f; info.avg15 = static_cast(_last15Total) / _usages15.size() / 100.0f; #elif defined(__sun) || defined(__linux) || defined(__APPLE__) // // We use the load average divided by the number of // processors to figure out if the machine is busy or // not. The result is capped at 1.0f. // double loadAvg[3]; if(getloadavg(loadAvg, 3) != -1) { info.avg1 = static_cast(loadAvg[0]); info.avg5 = static_cast(loadAvg[1]); info.avg15 = static_cast(loadAvg[2]); } #elif defined(__hpux) struct pst_dynamic dynInfo; if(pstat_getdynamic(&dynInfo, sizeof(dynInfo), 1, 0) >= 0) { info.avg1 = dynInfo.psd_avg_1_min; info.avg5 = dynInfo.psd_avg_5_min; info.avg15 = dynInfo.psd_avg_15_min; } #elif defined(_AIX) if(_kmem > 1) { long long avenrun[3]; struct nlist nl; nl.n_name = "avenrun"; nl.n_value = 0; if(knlist(&nl, 1, sizeof(nl)) == 0) { if(pread(_kmem, avenrun, sizeof(avenrun), nl.n_value) >= sizeof(avenrun)) { info.avg1 = avenrun[0] / 65536.0f; info.avg5 = avenrun[1] / 65536.0f; info.avg15 = avenrun[2] / 65536.0f; } } } #endif return info; } std::string PlatformInfo::getHostname() const { return _hostname; } std::string PlatformInfo::getDataDir() const { return _dataDir; } std::string PlatformInfo::getCwd() const { return _cwd; } #ifdef _WIN32 void PlatformInfo::runUpdateLoadInfo() { // // NOTE: We shouldn't initialize the performance counter from the // PlatformInfo constructor because it might be called when // IceGrid is started on boot as a Windows service with the // Windows service control manager (SCM) locked. The query // initialization would fail (hang) because it requires to start // the "WMI Windows Adapter" service (which can't be started // because the SCM is locked...). // // // Open the query. // HQUERY query; PDH_STATUS err = PdhOpenQuery(0, 0, &query); if(err != ERROR_SUCCESS) { Ice::Warning out(_traceLevels->logger); out << "Cannot open performance data query:\n" << pdhErrorToString(err); return; } // // Add the counter for \\Processor(_Total)\\"%Processor Time". // // We have to look up the localized names for these. "Processor" // is index 238 and "%Processor Time" is index 6. // // If either lookup fails, close the query system, and we're done. // string processor; string percentProcessorTime; try { processor = getLocalizedPerfName(238, _traceLevels->logger); percentProcessorTime = getLocalizedPerfName(6, _traceLevels->logger); } catch(const Ice::LocalException&) { // No need to print a warning, it's taken care of by getLocalizedPerfName PdhCloseQuery(query); return; } const string name = "\\" + processor + "(_Total)\\" + percentProcessorTime; HCOUNTER _counter; err = PdhAddCounter(query, name.c_str(), 0, &_counter); if(err != ERROR_SUCCESS) { Ice::Warning out(_traceLevels->logger); out << "Cannot add performance counter `" + name + "' (expected "; out << "if you have insufficient privileges to monitor performance counters):\n"; out << pdhErrorToString(err); PdhCloseQuery(query); return; } while(true) { IceUtil::Monitor::Lock sync(_utilizationMonitor); _utilizationMonitor.timedWait(IceUtil::Time::seconds(5)); // 5 seconds. if(_terminated) { break; } int usage = 100; PDH_STATUS err = PdhCollectQueryData(query); if(err == ERROR_SUCCESS) { DWORD type; PDH_FMT_COUNTERVALUE value; PdhGetFormattedCounterValue(_counter, PDH_FMT_LONG, &type, &value); usage = static_cast(value.longValue); } else { Ice::Warning out(_traceLevels->logger); out << "Could not collect performance counter data:\n" << pdhErrorToString(err); } _last1Total += usage - _usages1.back(); _last5Total += usage - _usages5.back(); _last15Total += usage - _usages15.back(); _usages1.pop_back(); _usages5.pop_back(); _usages15.pop_back(); _usages1.push_front(usage); _usages5.push_front(usage); _usages15.push_front(usage); } PdhCloseQuery(query); } #endif