Raise specific exception types for better error handling
raise Exception('did not start with "cpu"')
1#!/usr/bin/env python32# ignore-tidy-file-linelength34# This is a small script that we use on CI to collect CPU usage statistics of5# our builders. By seeing graphs of CPU usage over time we hope to correlate6# that with possible improvements to Rust's own build system, ideally diagnosing7# that either builders are always fully using their CPU resources or they're8# idle for long stretches of time.9#10# This script is relatively simple, but it's platform specific. Each platform11# (OSX/Windows/Linux) has a different way of calculating the current state of12# CPU at a point in time. We then compare two captured states to determine the13# percentage of time spent in one state versus another. The state capturing is14# all platform-specific but the loop at the bottom is the cross platform part15# that executes everywhere.16#17# # Viewing statistics18#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.csv23#24# for example25#26# https://rust-lang-ci2.s3.amazonaws.com/rustc-builds/68baada19cd5340f05f0db15a3e16d6671609bcc/cpu-x86_64-apple.csv27#28# Each CSV file has two columns. The first is the timestamp of the measurement29# and the second column is the % of idle cpu time in that time slice. Ideally30# the second column is always zero.31#32# Once you've downloaded a file there's various ways to plot it and visualize33# it. For command line usage you use the `src/etc/cpu-usage-over-time-plot.sh`34# script in this repository.3536import datetime37import sys38import time3940# Python 3.3 changed the value of `sys.platform` on Linux from "linux2" to just41# "linux". We check here with `.startswith` to keep compatibility with older42# Python versions (especially Python 2.7).43if sys.platform.startswith("linux"):4445 class State:46 def __init__(self):47 with open("/proc/stat", "r") as file:48 data = file.readline().split()49 if data[0] != "cpu":50 raise Exception('did not start with "cpu"')51 self.user = int(data[1])52 self.nice = int(data[2])53 self.system = int(data[3])54 self.idle = int(data[4])55 self.iowait = int(data[5])56 self.irq = int(data[6])57 self.softirq = int(data[7])58 self.steal = int(data[8])59 self.guest = int(data[9])60 self.guest_nice = int(data[10])6162 def idle_since(self, prev):63 user = self.user - prev.user64 nice = self.nice - prev.nice65 system = self.system - prev.system66 idle = self.idle - prev.idle67 iowait = self.iowait - prev.iowait68 irq = self.irq - prev.irq69 softirq = self.softirq - prev.softirq70 steal = self.steal - prev.steal71 guest = self.guest - prev.guest72 guest_nice = self.guest_nice - prev.guest_nice73 total = (74 user75 + nice76 + system77 + idle78 + iowait79 + irq80 + softirq81 + steal82 + guest83 + guest_nice84 )85 return float(idle) / float(total) * 1008687elif sys.platform == "win32":88 from ctypes.wintypes import DWORD89 from ctypes import Structure, windll, WinError, GetLastError, byref9091 class FILETIME(Structure):92 _fields_ = [93 ("dwLowDateTime", DWORD),94 ("dwHighDateTime", DWORD),95 ]9697 class State:98 def __init__(self):99 idle, kernel, user = FILETIME(), FILETIME(), FILETIME()100101 success = windll.kernel32.GetSystemTimes(102 byref(idle),103 byref(kernel),104 byref(user),105 )106107 assert success, WinError(GetLastError())[1]108109 self.idle = (idle.dwHighDateTime << 32) | idle.dwLowDateTime110 self.kernel = (kernel.dwHighDateTime << 32) | kernel.dwLowDateTime111 self.user = (user.dwHighDateTime << 32) | user.dwLowDateTime112113 def idle_since(self, prev):114 idle = self.idle - prev.idle115 user = self.user - prev.user116 kernel = self.kernel - prev.kernel117 return float(idle) / float(user + kernel) * 100118119elif sys.platform == "darwin":120 from ctypes import *121122 libc = cdll.LoadLibrary("/usr/lib/libc.dylib")123124 class host_cpu_load_info_data_t(Structure):125 _fields_ = [("cpu_ticks", c_uint * 4)]126127 host_statistics = libc.host_statistics128 host_statistics.argtypes = [129 c_uint,130 c_int,131 POINTER(host_cpu_load_info_data_t),132 POINTER(c_int),133 ]134 host_statistics.restype = c_int135136 CPU_STATE_USER = 0137 CPU_STATE_SYSTEM = 1138 CPU_STATE_IDLE = 2139 CPU_STATE_NICE = 3140141 class State:142 def __init__(self):143 stats = host_cpu_load_info_data_t()144 count = c_int(4) # HOST_CPU_LOAD_INFO_COUNT145 err = libc.host_statistics(146 libc.mach_host_self(),147 c_int(3), # HOST_CPU_LOAD_INFO148 byref(stats),149 byref(count),150 )151 assert err == 0152 self.system = stats.cpu_ticks[CPU_STATE_SYSTEM]153 self.user = stats.cpu_ticks[CPU_STATE_USER]154 self.idle = stats.cpu_ticks[CPU_STATE_IDLE]155 self.nice = stats.cpu_ticks[CPU_STATE_NICE]156157 def idle_since(self, prev):158 user = self.user - prev.user159 system = self.system - prev.system160 idle = self.idle - prev.idle161 nice = self.nice - prev.nice162 return float(idle) / float(user + system + idle + nice) * 100.0163164else:165 print("unknown platform", sys.platform)166 sys.exit(1)167168cur_state = State()169print("Time,Idle")170while True:171 time.sleep(1)172 next_state = State()173 now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None).isoformat()174 idle = next_state.idle_since(cur_state)175 print("%s,%s" % (now, idle))176 sys.stdout.flush()177 cur_state = next_state
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.