2 # ignore-tidy-linelength
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.
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.
17 # # Viewing statistics
19 # All builders will upload their CPU statistics as CSV files to our S3 buckets.
20 # These URLS look like:
22 # https://$bucket.s3.amazonaws.com/rustc-builds/$commit/cpu-$builder.csv
26 # https://rust-lang-ci2.s3.amazonaws.com/rustc-builds/68baada19cd5340f05f0db15a3e16d6671609bcc/cpu-x86_64-apple.csv
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.
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:
35 # set timefmt '%Y-%m-%dT%H:%M:%S'
37 # set ylabel "Idle CPU %"
39 # set datafile sep ','
41 # set output "printme.png"
43 # builder = "i686-apple"
44 # plot "cpu-".builder.".csv" using 1:2 with lines title builder
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!)
55 if sys.platform == 'linux2':
58 with open('/proc/stat', 'r') as file:
59 data = file.readline().split()
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])
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
87 elif sys.platform == 'win32':
88 from ctypes.wintypes import DWORD
89 from ctypes import Structure, windll, WinError, GetLastError, byref
91 class FILETIME(Structure):
93 ("dwLowDateTime", DWORD),
94 ("dwHighDateTime", DWORD),
99 idle, kernel, user = FILETIME(), FILETIME(), FILETIME()
101 success = windll.kernel32.GetSystemTimes(
107 assert success, WinError(GetLastError())[1]
109 self.idle = (idle.dwHighDateTime << 32) | idle.dwLowDateTime
110 self.kernel = (kernel.dwHighDateTime << 32) | kernel.dwLowDateTime
111 self.user = (user.dwHighDateTime << 32) | user.dwLowDateTime
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
119 elif sys.platform == 'darwin':
121 libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
123 PROESSOR_CPU_LOAD_INFO = c_int(2)
128 c_int_p = POINTER(c_int)
132 num_cpus_u = c_uint(0)
134 cpu_info_cnt = c_int(0)
135 err = libc.host_processor_info(
136 libc.mach_host_self(),
137 PROESSOR_CPU_LOAD_INFO,
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
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
163 print('unknown platform', sys.platform)
170 next_state = State();
171 now = datetime.datetime.utcnow().isoformat()
172 idle = next_state.idle_since(cur_state)
173 print("%s,%s" % (now, idle))
175 cur_state = next_state