]> git.lizzy.rs Git - rust.git/blob - src/ci/cpu-usage-over-time.py
Rollup merge of #61398 - kennytm:stabilize-copy-within, r=SimonSapin
[rust.git] / src / ci / cpu-usage-over-time.py
1 #!/usr/bin/env python
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 can use a script like so:
34 #
35 #      set timefmt '%Y-%m-%dT%H:%M:%S'
36 #      set xdata time
37 #      set ylabel "Idle CPU %"
38 #      set xlabel "Time"
39 #      set datafile sep ','
40 #      set term png
41 #      set output "printme.png"
42 #      set grid
43 #      builder = "i686-apple"
44 #      plot "cpu-".builder.".csv" using 1:2 with lines title builder
45 #
46 # Executed as `gnuplot < ./foo.plot` it will generate a graph called
47 # `printme.png` which you can then open up. If you know how to improve this
48 # script or the viewing process that would be much appreciated :) (or even if
49 # you know how to automate it!)
50
51 import datetime
52 import sys
53 import time
54
55 if sys.platform == 'linux2':
56     class State:
57         def __init__(self):
58             with open('/proc/stat', 'r') as file:
59                 data = file.readline().split()
60             if data[0] != 'cpu':
61                 raise Exception('did not start with "cpu"')
62             self.user = int(data[1])
63             self.nice = int(data[2])
64             self.system = int(data[3])
65             self.idle = int(data[4])
66             self.iowait = int(data[5])
67             self.irq = int(data[6])
68             self.softirq = int(data[7])
69             self.steal = int(data[8])
70             self.guest = int(data[9])
71             self.guest_nice = int(data[10])
72
73         def idle_since(self, prev):
74             user = self.user - prev.user
75             nice = self.nice - prev.nice
76             system = self.system - prev.system
77             idle = self.idle - prev.idle
78             iowait = self.iowait - prev.iowait
79             irq = self.irq - prev.irq
80             softirq = self.softirq - prev.softirq
81             steal = self.steal - prev.steal
82             guest = self.guest - prev.guest
83             guest_nice = self.guest_nice - prev.guest_nice
84             total = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice
85             return float(idle) / float(total) * 100
86
87 elif sys.platform == 'win32':
88     from ctypes.wintypes import DWORD
89     from ctypes import Structure, windll, WinError, GetLastError, byref
90
91     class FILETIME(Structure):
92         _fields_ = [
93             ("dwLowDateTime", DWORD),
94             ("dwHighDateTime", DWORD),
95         ]
96
97     class State:
98         def __init__(self):
99             idle, kernel, user = FILETIME(), FILETIME(), FILETIME()
100
101             success = windll.kernel32.GetSystemTimes(
102                 byref(idle),
103                 byref(kernel),
104                 byref(user),
105             )
106
107             assert success, WinError(GetLastError())[1]
108
109             self.idle = (idle.dwHighDateTime << 32) | idle.dwLowDateTime
110             self.kernel = (kernel.dwHighDateTime << 32) | kernel.dwLowDateTime
111             self.user = (user.dwHighDateTime << 32) | user.dwLowDateTime
112
113         def idle_since(self, prev):
114             idle = self.idle - prev.idle
115             user = self.user - prev.user
116             kernel = self.kernel - prev.kernel
117             return float(idle) / float(user + kernel) * 100
118
119 elif sys.platform == 'darwin':
120     from ctypes import *
121     libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
122
123     PROESSOR_CPU_LOAD_INFO = c_int(2)
124     CPU_STATE_USER = 0
125     CPU_STATE_SYSTEM = 1
126     CPU_STATE_IDLE = 2
127     CPU_STATE_NICE = 3
128     c_int_p = POINTER(c_int)
129
130     class State:
131         def __init__(self):
132             num_cpus_u = c_uint(0)
133             cpu_info = c_int_p()
134             cpu_info_cnt = c_int(0)
135             err = libc.host_processor_info(
136                 libc.mach_host_self(),
137                 PROESSOR_CPU_LOAD_INFO,
138                 byref(num_cpus_u),
139                 byref(cpu_info),
140                 byref(cpu_info_cnt),
141             )
142             assert err == 0
143             self.user = 0
144             self.system = 0
145             self.idle = 0
146             self.nice = 0
147             cur = 0
148             while cur < cpu_info_cnt.value:
149                 self.user += cpu_info[cur + CPU_STATE_USER]
150                 self.system += cpu_info[cur + CPU_STATE_SYSTEM]
151                 self.idle += cpu_info[cur + CPU_STATE_IDLE]
152                 self.nice += cpu_info[cur + CPU_STATE_NICE]
153                 cur += num_cpus_u.value
154
155         def idle_since(self, prev):
156             user = self.user - prev.user
157             system = self.system - prev.system
158             idle = self.idle - prev.idle
159             nice = self.nice - prev.nice
160             return float(idle) / float(user + system + idle + nice) * 100.0
161
162 else:
163     print('unknown platform', sys.platform)
164     sys.exit(1)
165
166 cur_state = State();
167 print("Time,Idle")
168 while True:
169     time.sleep(1);
170     next_state = State();
171     now = datetime.datetime.utcnow().isoformat()
172     idle = next_state.idle_since(cur_state)
173     print("%s,%s" % (now, idle))
174     sys.stdout.flush()
175     cur_state = next_state