Use Optional::valueOr() instead of Optional::value_or()
[WebKit-https.git] / Source / WebCore / page / linux / ResourceUsageThreadLinux.cpp
1 /*
2  * Copyright (C) 2017 Igalia S.L.
3  * Copyright (C) 2018 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "ResourceUsageThread.h"
29
30 #if ENABLE(RESOURCE_USAGE) && OS(LINUX)
31
32 #include <JavaScriptCore/GCActivityCallback.h>
33 #include <JavaScriptCore/VM.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <unistd.h>
41 #include <wtf/linux/CurrentProcessMemoryStatus.h>
42
43 namespace WebCore {
44
45 static float cpuPeriod()
46 {
47     FILE* file = fopen("/proc/stat", "r");
48     if (!file)
49         return 0;
50
51     static const unsigned statMaxLineLength = 512;
52     char buffer[statMaxLineLength + 1];
53     char* line = fgets(buffer, statMaxLineLength, file);
54     if (!line) {
55         fclose(file);
56         return 0;
57     }
58
59     unsigned long long userTime, niceTime, systemTime, idleTime;
60     unsigned long long ioWait, irq, softIrq, steal, guest, guestnice;
61     ioWait = irq = softIrq = steal = guest = guestnice = 0;
62     int retVal = sscanf(buffer, "cpu  %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu",
63         &userTime, &niceTime, &systemTime, &idleTime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
64     // We expect 10 values to be matched by sscanf
65     if (retVal != 10) {
66         fclose(file);
67         return 0;
68     }
69
70
71     // Keep parsing if we still don't know cpuCount.
72     static unsigned cpuCount = 0;
73     if (!cpuCount) {
74         while ((line = fgets(buffer, statMaxLineLength, file))) {
75             if (strlen(line) > 4 && line[0] == 'c' && line[1] == 'p' && line[2] == 'u')
76                 cpuCount++;
77             else
78                 break;
79         }
80     }
81     fclose(file);
82
83     if (!cpuCount)
84         return 0;
85
86     static unsigned long long previousTotalTime = 0;
87     unsigned long long totalTime = userTime + niceTime + systemTime + irq + softIrq + idleTime + ioWait + steal;
88     unsigned long long period = totalTime > previousTotalTime ? totalTime - previousTotalTime : 0;
89     previousTotalTime = totalTime;
90     return static_cast<float>(period) / cpuCount;
91 }
92
93 static float cpuUsage()
94 {
95     float period = cpuPeriod();
96     if (!period)
97         return -1;
98
99     int fd = open("/proc/self/stat", O_RDONLY);
100     if (fd < 0)
101         return -1;
102
103     static const ssize_t maxBufferLength = BUFSIZ - 1;
104     char buffer[BUFSIZ];
105     buffer[0] = '\0';
106
107     ssize_t totalBytesRead = 0;
108     while (totalBytesRead < maxBufferLength) {
109         ssize_t bytesRead = read(fd, buffer + totalBytesRead, maxBufferLength - totalBytesRead);
110         if (bytesRead < 0) {
111             if (errno != EINTR) {
112                 close(fd);
113                 return -1;
114             }
115             continue;
116         }
117
118         if (!bytesRead)
119             break;
120
121         totalBytesRead += bytesRead;
122     }
123     close(fd);
124     buffer[totalBytesRead] = '\0';
125
126     // Skip pid and process name.
127     char* position = strrchr(buffer, ')');
128     if (!position)
129         return -1;
130
131     // Move after state.
132     position += 4;
133
134     // Skip ppid, pgrp, sid, tty_nr, tty_pgrp, flags, min_flt, cmin_flt, maj_flt, cmaj_flt.
135     unsigned tokensToSkip = 10;
136     while (tokensToSkip--) {
137         while (!isASCIISpace(position[0]))
138             position++;
139         position++;
140     }
141
142     static unsigned long long previousUtime = 0;
143     static unsigned long long previousStime = 0;
144     unsigned long long utime = strtoull(position, &position, 10);
145     unsigned long long stime = strtoull(position, &position, 10);
146     float usage = (utime + stime - (previousUtime + previousStime)) / period * 100.0;
147     previousUtime = utime;
148     previousStime = stime;
149
150     return clampTo<float>(usage, 0, 100);
151 }
152
153 void ResourceUsageThread::platformThreadBody(JSC::VM* vm, ResourceUsageData& data)
154 {
155     data.cpu = cpuUsage();
156
157     ProcessMemoryStatus memoryStatus;
158     currentProcessMemoryStatus(memoryStatus);
159     data.totalDirtySize = memoryStatus.resident - memoryStatus.shared;
160
161     size_t currentGCHeapCapacity = vm->heap.blockBytesAllocated();
162     size_t currentGCOwnedExtra = vm->heap.extraMemorySize();
163     size_t currentGCOwnedExternal = vm->heap.externalMemorySize();
164     RELEASE_ASSERT(currentGCOwnedExternal <= currentGCOwnedExtra);
165
166     data.categories[MemoryCategory::GCHeap].dirtySize = currentGCHeapCapacity;
167     data.categories[MemoryCategory::GCOwned].dirtySize = currentGCOwnedExtra - currentGCOwnedExternal;
168     data.categories[MemoryCategory::GCOwned].externalSize = currentGCOwnedExternal;
169
170     data.totalExternalSize = currentGCOwnedExternal;
171
172     auto now = MonotonicTime::now();
173     data.timeOfNextEdenCollection = now + vm->heap.edenActivityCallback()->timeUntilFire().valueOr(Seconds(std::numeric_limits<double>::infinity()));
174     data.timeOfNextFullCollection = now + vm->heap.fullActivityCallback()->timeUntilFire().valueOr(Seconds(std::numeric_limits<double>::infinity()));
175 }
176
177 } // namespace WebCore
178
179 #endif // ENABLE(RESOURCE_USAGE) && OS(LINUX)