]> git.lizzy.rs Git - rust.git/blob - src/ci/cpu-usage-over-time.py
Sync portable-simd to rust-lang/portable-simd@72df4c45056a8bc0d1b3f06fdc828722177f0763
[rust.git] / src / ci / cpu-usage-over-time.py
1 #!/usr/bin/env python3
2 # ignore-tidy-linelength
3
4 # This is a small script that we use on CI to collect CPU usage statistics of
5 # our builders. By seeing graphs of CPU usage over time we hope to correlate
6 # that with possible improvements to Rust's own build system, ideally diagnosing
7 # that either builders are always fully using their CPU resources or they're
8 # idle for long stretches of time.
9 #
10 # This script is relatively simple, but it's platform specific. Each platform
11 # (OSX/Windows/Linux) has a different way of calculating the current state of
12 # CPU at a point in time. We then compare two captured states to determine the
13 # percentage of time spent in one state versus another. The state capturing is
14 # all platform-specific but the loop at the bottom is the cross platform part
15 # that executes everywhere.
16 #
17 # # Viewing statistics
18 #
19 # All builders will upload their CPU statistics as CSV files to our S3 buckets.
20 # These URLS look like:
21 #
22 #   https://$bucket.s3.amazonaws.com/rustc-builds/$commit/cpu-$builder.csv
23 #
24 # for example
25 #
26 #   https://rust-lang-ci2.s3.amazonaws.com/rustc-builds/68baada19cd5340f05f0db15a3e16d6671609bcc/cpu-x86_64-apple.csv
27 #
28 # Each CSV file has two columns. The first is the timestamp of the measurement
29 # and the second column is the % of idle cpu time in that time slice. Ideally
30 # the second column is always zero.
31 #
32 # Once you've downloaded a file there's various ways to plot it and visualize
33 # it. For command line usage you use the `src/etc/cpu-usage-over-time-plot.sh`
34 # script in this repository.
35
36 import datetime
37 import sys
38 import time
39
40 # Python 3.3 changed the value of `sys.platform` on Linux from "linux2" to just
41 # "linux". We check here with `.startswith` to keep compatibility with older
42 # Python versions (especially Python 2.7).
43 if sys.platform.startswith('linux'):
44     class State:
45         def __init__(self):
46             with open('/proc/stat', 'r') as file:
47                 data = file.readline().split()
48             if data[0] != 'cpu':
49                 raise Exception('did not start with "cpu"')
50             self.user = int(data[1])
51             self.nice = int(data[2])
52             self.system = int(data[3])
53             self.idle = int(data[4])
54             self.iowait = int(data[5])
55             self.irq = int(data[6])
56             self.softirq = int(data[7])
57             self.steal = int(data[8])
58             self.guest = int(data[9])
59             self.guest_nice = int(data[10])
60
61         def idle_since(self, prev):
62             user = self.user - prev.user
63             nice = self.nice - prev.nice
64             system = self.system - prev.system
65             idle = self.idle - prev.idle
66             iowait = self.iowait - prev.iowait
67             irq = self.irq - prev.irq
68             softirq = self.softirq - prev.softirq
69             steal = self.steal - prev.steal
70             guest = self.guest - prev.guest
71             guest_nice = self.guest_nice - prev.guest_nice
72             total = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice
73             return float(idle) / float(total) * 100
74
75 elif sys.platform == 'win32':
76     from ctypes.wintypes import DWORD
77     from ctypes import Structure, windll, WinError, GetLastError, byref
78
79     class FILETIME(Structure):
80         _fields_ = [
81             ("dwLowDateTime", DWORD),
82             ("dwHighDateTime", DWORD),
83         ]
84
85     class State:
86         def __init__(self):
87             idle, kernel, user = FILETIME(), FILETIME(), FILETIME()
88
89             success = windll.kernel32.GetSystemTimes(
90                 byref(idle),
91                 byref(kernel),
92                 byref(user),
93             )
94
95             assert success, WinError(GetLastError())[1]
96
97             self.idle = (idle.dwHighDateTime << 32) | idle.dwLowDateTime
98             self.kernel = (kernel.dwHighDateTime << 32) | kernel.dwLowDateTime
99             self.user = (user.dwHighDateTime << 32) | user.dwLowDateTime
100
101         def idle_since(self, prev):
102             idle = self.idle - prev.idle
103             user = self.user - prev.user
104             kernel = self.kernel - prev.kernel
105             return float(idle) / float(user + kernel) * 100
106
107 elif sys.platform == 'darwin':
108     from ctypes import *
109     libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
110
111     class host_cpu_load_info_data_t(Structure):
112         _fields_ = [("cpu_ticks", c_uint * 4)]
113
114     host_statistics = libc.host_statistics
115     host_statistics.argtypes = [
116         c_uint,
117         c_int,
118         POINTER(host_cpu_load_info_data_t),
119         POINTER(c_int)
120     ]
121     host_statistics.restype = c_int
122
123     CPU_STATE_USER = 0
124     CPU_STATE_SYSTEM = 1
125     CPU_STATE_IDLE = 2
126     CPU_STATE_NICE = 3
127     class State:
128         def __init__(self):
129             stats = host_cpu_load_info_data_t()
130             count = c_int(4) # HOST_CPU_LOAD_INFO_COUNT
131             err = libc.host_statistics(
132                 libc.mach_host_self(),
133                 c_int(3), # HOST_CPU_LOAD_INFO
134                 byref(stats),
135                 byref(count),
136             )
137             assert err == 0
138             self.system = stats.cpu_ticks[CPU_STATE_SYSTEM]
139             self.user = stats.cpu_ticks[CPU_STATE_USER]
140             self.idle = stats.cpu_ticks[CPU_STATE_IDLE]
141             self.nice = stats.cpu_ticks[CPU_STATE_NICE]
142
143         def idle_since(self, prev):
144             user = self.user - prev.user
145             system = self.system - prev.system
146             idle = self.idle - prev.idle
147             nice = self.nice - prev.nice
148             return float(idle) / float(user + system + idle + nice) * 100.0
149
150 else:
151     print('unknown platform', sys.platform)
152     sys.exit(1)
153
154 cur_state = State()
155 print("Time,Idle")
156 while True:
157     time.sleep(1)
158     next_state = State()
159     now = datetime.datetime.utcnow().isoformat()
160     idle = next_state.idle_since(cur_state)
161     print("%s,%s" % (now, idle))
162     sys.stdout.flush()
163     cur_state = next_state