/* $Id: PerformanceLinux.cpp 27950 2010-04-01 17:07:40Z vboxsync $ */ /** @file * * VBox Linux-specific Performance Classes implementation. */ /* * Copyright (C) 2008 Sun Microsystems, Inc. * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 USA or visit http://www.sun.com if you need * additional information or have any questions. */ #include #include #include #include #include #include #include #include "Logging.h" #include "Performance.h" namespace pm { class CollectorLinux : public CollectorHAL { public: virtual int preCollect(const CollectorHints& hints, uint64_t /* iTick */); virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available); virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used); virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle); virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total); private: virtual int _getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle); int getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed); struct VMProcessStats { uint64_t cpuUser; uint64_t cpuKernel; ULONG pagesUsed; }; typedef std::map VMProcessMap; VMProcessMap mProcessStats; uint64_t mUser, mKernel, mIdle; }; CollectorHAL *createHAL() { return new CollectorLinux(); } // Collector HAL for Linux int CollectorLinux::preCollect(const CollectorHints& hints, uint64_t /* iTick */) { std::vector processes; hints.getProcesses(processes); std::vector::iterator it; for(it = processes.begin(); it != processes.end(); it++) { VMProcessStats vmStats; int rc = getRawProcessStats(*it, &vmStats.cpuUser, &vmStats.cpuKernel, &vmStats.pagesUsed); if (RT_FAILURE(rc)) return rc; mProcessStats[*it] = vmStats; } if (hints.isHostCpuLoadCollected() || mProcessStats.size()) { _getRawHostCpuLoad(&mUser, &mKernel, &mIdle); } return VINF_SUCCESS; } int CollectorLinux::_getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle) { int rc = VINF_SUCCESS; ULONG u32user, u32nice, u32kernel, u32idle; FILE *f = fopen("/proc/stat", "r"); if (f) { if (fscanf(f, "cpu %u %u %u %u", &u32user, &u32nice, &u32kernel, &u32idle) == 4) { *user = (uint64_t)u32user + u32nice; *kernel = u32kernel; *idle = u32idle; } else rc = VERR_FILE_IO_ERROR; fclose(f); } else rc = VERR_ACCESS_DENIED; return rc; } int CollectorLinux::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle) { *user = mUser; *kernel = mKernel; *idle = mIdle; return VINF_SUCCESS; } int CollectorLinux::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total) { VMProcessMap::const_iterator it = mProcessStats.find(process); if (it == mProcessStats.end()) { Log (("No stats pre-collected for process %x\n", process)); return VERR_INTERNAL_ERROR; } *user = it->second.cpuUser; *kernel = it->second.cpuKernel; *total = mUser + mKernel + mIdle; return VINF_SUCCESS; } int CollectorLinux::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) { int rc = VINF_SUCCESS; ULONG buffers, cached; FILE *f = fopen("/proc/meminfo", "r"); if (f) { int processed = fscanf(f, "MemTotal: %u kB\n", total); processed += fscanf(f, "MemFree: %u kB\n", available); processed += fscanf(f, "Buffers: %u kB\n", &buffers); processed += fscanf(f, "Cached: %u kB\n", &cached); if (processed == 4) { *available += buffers + cached; *used = *total - *available; } else rc = VERR_FILE_IO_ERROR; fclose(f); } else rc = VERR_ACCESS_DENIED; return rc; } int CollectorLinux::getProcessMemoryUsage(RTPROCESS process, ULONG *used) { VMProcessMap::const_iterator it = mProcessStats.find(process); if (it == mProcessStats.end()) { Log (("No stats pre-collected for process %x\n", process)); return VERR_INTERNAL_ERROR; } *used = it->second.pagesUsed * (PAGE_SIZE / 1024); return VINF_SUCCESS; } int CollectorLinux::getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed) { int rc = VINF_SUCCESS; char *pszName; pid_t pid2; char c; int iTmp; long long unsigned int u64Tmp; unsigned uTmp; unsigned long ulTmp; ULONG u32user, u32kernel; char buf[80]; /* @todo: this should be tied to max allowed proc name. */ RTStrAPrintf(&pszName, "/proc/%d/stat", process); //printf("Opening %s...\n", pszName); FILE *f = fopen(pszName, "r"); RTMemFree(pszName); if (f) { if (fscanf(f, "%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %u %u " "%ld %ld %ld %ld %ld %ld %llu %lu %u", &pid2, buf, &c, &iTmp, &iTmp, &iTmp, &iTmp, &iTmp, &uTmp, &ulTmp, &ulTmp, &ulTmp, &ulTmp, &u32user, &u32kernel, &ulTmp, &ulTmp, &ulTmp, &ulTmp, &ulTmp, &ulTmp, &u64Tmp, &ulTmp, memPagesUsed) == 24) { Assert((pid_t)process == pid2); *cpuUser = u32user; *cpuKernel = u32kernel; } else rc = VERR_FILE_IO_ERROR; fclose(f); } else rc = VERR_ACCESS_DENIED; return rc; } }