blob: b4cb2e24d76e84186ab21ecc931f7216e0f8bcfa [file] [log] [blame]
// Copyright 2013 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "machine_info.h"
#include <stdio.h>
#include <sys/types.h>
#include "basictypes.h"
#include "glog/logging.h"
#include "scoped_fd.h"
#include "util.h"
#if defined(_WIN32)
#include <psapi.h>
#include <windows.h>
#endif
#if defined(__linux__)
#include <unistd.h>
#endif
#if defined(__MACH__)
#include <libproc.h>
#include <sys/sysctl.h>
#include <sys/proc_info.h>
#endif
namespace devtools_goma {
#if defined(_WIN32)
int GetNumCPUs() {
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
}
int64_t GetSystemTotalMemory() {
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
if (!GlobalMemoryStatusEx(&status)) {
fprintf(stderr, "GlobalMemoryStatusEx failed: %lu\n", GetLastError());
return 0;
}
return status.ullTotalPhys;
}
int64_t GetConsumingMemoryOfCurrentProcess() {
DWORD process_id = GetCurrentProcessId();
ScopedFd process(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id));
if (!process.valid()) {
PLOG(ERROR) << "OpenProcess failed";
return 0;
}
PROCESS_MEMORY_COUNTERS pmc;
if (!GetProcessMemoryInfo(process.handle(), &pmc, sizeof(pmc))) {
PLOG(ERROR) << "GetProcessMemoryInfo failed";
return 0;
}
return pmc.WorkingSetSize;
}
#elif defined(__linux__)
int GetNumCPUs() {
int cpus = sysconf(_SC_NPROCESSORS_ONLN);
if (cpus < 0) {
PLOG(ERROR) << "sysconf(_SC_NPROCESSORS_ONLN) failed";
return 0;
}
return cpus;
}
int64_t GetSystemTotalMemory() {
const int64_t page_size = sysconf(_SC_PAGESIZE);
if (page_size < 0) {
PLOG(ERROR) << "sysconf(_SC_PAGESIZE) failed";
return 0;
}
const int64_t num_pages = sysconf(_SC_PHYS_PAGES);
if (num_pages < 0) {
PLOG(ERROR) << "sysconf(_SC_PHYS_PAGES) failed";
return 0;
}
return page_size * num_pages;
}
int64_t GetConsumingMemoryOfCurrentProcess() {
// Reads /proc/self/statm
// The second column is the number of pages for resident.
const int64_t page_size = sysconf(_SC_PAGESIZE);
if (page_size < 0) {
PLOG(ERROR) << "sysconf(_SC_PAGESIZE) failed";
return 0;
}
ScopedFd fd(ScopedFd::OpenForRead("/proc/self/statm"));
if (!fd.valid()) {
PLOG(ERROR) << "Opening /proc/self/statm failed";
return 0;
}
char buf[1024];
if (fd.Read(buf, 1024) < 0) {
PLOG(ERROR) << "Reading /proc/self/statm failed";
return 0;
}
int num_pages;
if (sscanf(buf, "%*d %d", &num_pages) != 1) {
LOG(ERROR) << "Data from /proc/self/statm is not in expected form";
return 0;
}
return num_pages * page_size;
}
#elif defined(__MACH__)
int GetNumCPUs() {
static const char* kCandidates[] = {
"hw.logicalcpu_max", "hw.ncpu"
};
int size = 0;
size_t len = sizeof(size);
for (const auto& candidate : kCandidates) {
if (sysctlbyname(candidate, &size, &len, nullptr, 0) == 0) {
return size;
}
}
// Failed for all candidates.
LOG(ERROR) << "sysctlbyname for GetNumCPUs failed";
return 0;
}
int64_t GetSystemTotalMemory() {
int64_t size;
size_t len = sizeof(size);
if (sysctlbyname("hw.memsize", &size, &len, nullptr, 0) < 0) {
PLOG(ERROR) << "sysctlbyname(hw.memsize) failed";
return 0;
}
return size;
}
int64_t GetConsumingMemoryOfCurrentProcess() {
const pid_t pid = Getpid();
struct proc_taskinfo taskinfo;
int infosize = proc_pidinfo(pid, PROC_PIDTASKINFO, 0,
&taskinfo, sizeof(taskinfo));
if (infosize < 0) {
PLOG(ERROR) << "proc_pidinfo failed";
return 0;
}
// According to this blog,
// http://vinceyuan.blogspot.jp/2011/12/wrong-info-from-procpidinfo.html
// we have to check proc_pidinfo returning value. Sometimes proc_pidinfo
// returns too few bytes.
if (infosize < sizeof(taskinfo)) {
LOG(ERROR) << "proc_pidinfo returned too few bytes " << infosize
<< " (expected " << sizeof(taskinfo) << ")";
return 0;
}
return taskinfo.pti_resident_size;
}
#else
# error "Unknown architecture"
#endif
} // namespace devtools_goma