/* * Copyright (C) 2017 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ResourceUsageThread.h" #if ENABLE(RESOURCE_USAGE) && OS(LINUX) #include "CurrentProcessMemoryStatus.h" #include #include #include #include #include #include #include #include #include namespace WebCore { static float cpuPeriod() { FILE* file = fopen("/proc/stat", "r"); if (!file) return 0; static const unsigned statMaxLineLength = 512; char buffer[statMaxLineLength + 1]; char* line = fgets(buffer, statMaxLineLength, file); if (!line) { fclose(file); return 0; } unsigned long long userTime, niceTime, systemTime, idleTime; unsigned long long ioWait, irq, softIrq, steal, guest, guestnice; ioWait = irq = softIrq = steal = guest = guestnice = 0; sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &userTime, &niceTime, &systemTime, &idleTime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); // Keep parsing if we still don't know cpuCount. static unsigned cpuCount = 0; if (!cpuCount) { while ((line = fgets(buffer, statMaxLineLength, file))) { if (strlen(line) > 4 && line[0] == 'c' && line[1] == 'p' && line[2] == 'u') cpuCount++; else break; } } fclose(file); if (!cpuCount) return 0; static unsigned long long previousTotalTime = 0; unsigned long long totalTime = userTime + niceTime + systemTime + irq + softIrq + idleTime + ioWait + steal; unsigned long long period = totalTime > previousTotalTime ? totalTime - previousTotalTime : 0; previousTotalTime = totalTime; return static_cast(period) / cpuCount; } static float cpuUsage() { float period = cpuPeriod(); if (!period) return -1; int fd = open("/proc/self/stat", O_RDONLY); if (fd < 0) return -1; static const ssize_t maxBufferLength = BUFSIZ - 1; char buffer[BUFSIZ]; buffer[0] = '\0'; ssize_t totalBytesRead = 0; while (totalBytesRead < maxBufferLength) { ssize_t bytesRead = read(fd, buffer + totalBytesRead, maxBufferLength - totalBytesRead); if (bytesRead < 0) { if (errno != EINTR) { close(fd); return -1; } continue; } if (!bytesRead) break; totalBytesRead += bytesRead; } close(fd); buffer[totalBytesRead] = '\0'; // Skip pid and process name. char* position = strrchr(buffer, ')'); if (!position) return -1; // Move after state. position += 4; // Skip ppid, pgrp, sid, tty_nr, tty_pgrp, flags, min_flt, cmin_flt, maj_flt, cmaj_flt. unsigned tokensToSkip = 10; while (tokensToSkip--) { while (!isASCIISpace(position[0])) position++; position++; } static unsigned long long previousUtime = 0; static unsigned long long previousStime = 0; unsigned long long utime = strtoull(position, &position, 10); unsigned long long stime = strtoull(position, &position, 10); float usage = (utime + stime - (previousUtime + previousStime)) / period * 100.0; previousUtime = utime; previousStime = stime; return clampTo(usage, 0, 100); } void ResourceUsageThread::platformThreadBody(JSC::VM* vm, ResourceUsageData& data) { data.cpu = cpuUsage(); ProcessMemoryStatus memoryStatus; currentProcessMemoryStatus(memoryStatus); data.totalDirtySize = memoryStatus.resident - memoryStatus.shared; size_t currentGCHeapCapacity = vm->heap.blockBytesAllocated(); size_t currentGCOwnedExtra = vm->heap.extraMemorySize(); size_t currentGCOwnedExternal = vm->heap.externalMemorySize(); RELEASE_ASSERT(currentGCOwnedExternal <= currentGCOwnedExtra); data.categories[MemoryCategory::GCHeap].dirtySize = currentGCHeapCapacity; data.categories[MemoryCategory::GCOwned].dirtySize = currentGCOwnedExtra - currentGCOwnedExternal; data.categories[MemoryCategory::GCOwned].externalSize = currentGCOwnedExternal; data.totalExternalSize = currentGCOwnedExternal; data.timeOfNextEdenCollection = vm->heap.edenActivityCallback()->nextFireTime(); data.timeOfNextFullCollection = vm->heap.fullActivityCallback()->nextFireTime(); } } // namespace WebCore #endif // ENABLE(RESOURCE_USAGE) && OS(LINUX)