1#!/usr/bin/python3 2 3# Copyright (C) 2022 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17"""Utility to read CPU configurations from config file or device. 18""" 19 20import os 21import sys 22 23class CpuSettings: 24 def __init__(self): 25 self.allcores = [] # core id (int) 26 self.onlines = [] # CPU cores online 27 self.governor = "" 28 self.governors = {} # key: policy, value: governor 29 self.cpusets = {} # key: name, value: [] core number 30 31 def __str__(self): 32 strs = [] 33 strs.append("CpuSettings:{") 34 add_line_with_indentation(strs, "allcores:", 4) 35 strs.append(str(self.allcores)) 36 add_line_with_indentation(strs, "onlines:", 4) 37 strs.append(str(self.onlines)) 38 add_line_with_indentation(strs, "governor:", 4) 39 strs.append(str(self.governor)) 40 add_line_with_indentation(strs, "governors:[", 4) 41 for k in self.governors: 42 add_line_with_indentation(strs, str(k), 8) 43 strs.append('=') 44 strs.append(str(self.governors[k])) 45 strs.append("]") 46 add_line_with_indentation(strs, "cpusets:[", 4) 47 for k in self.cpusets: 48 add_line_with_indentation(strs, str(k), 8) 49 strs.append('=') 50 strs.append(str(self.cpusets[k])) 51 strs.append("]") 52 strs.append("}\n") 53 return ''.join(strs) 54 55class CpuConfig: 56 def __init__(self): 57 self.allcores = [] # core id (int) 58 self.coreMaxFreqKHz = {} # key: core id (int), # value: max freq (int) 59 self.configs = {} # key: name, value: CpuSettings 60 61 def __str__(self): 62 strs = [] 63 strs.append("CpuConfig:[") 64 add_line_with_indentation(strs, "allcores:", 2) 65 strs.append(str(self.allcores)) 66 add_line_with_indentation(strs, "coreMaxFreqKHz:", 2) 67 strs.append(str(self.coreMaxFreqKHz)) 68 for k in self.configs: 69 add_line_with_indentation(strs, str(k), 2) 70 strs.append(':') 71 strs.append(str(self.configs[k])) 72 strs.append("]") 73 return ''.join(strs) 74 75def parse_ints(word): 76 # valid inputs: 0-7, 0-1,4-7, 0,1 77 word = word.strip() 78 if word == "": 79 return [] 80 values = [] 81 pairs = word.split(',') 82 for pair in pairs: 83 min_max = pair.split('-') 84 if len(min_max) == 2: 85 min = int(min_max[0]) 86 max = int(min_max[1]) 87 if min >= max: 88 raise ValueError('min {} larger than max {}'.format(min, max)) 89 for i in range(min, max + 1): 90 values.append(i) 91 else: 92 values.append(int(pair)) 93 values.sort() 94 return values 95 96def parse_config(configFile): 97 lines = configFile.readlines() 98 config = CpuConfig() 99 allcores = [] 100 current_settings = None 101 default_governor = None 102 i = -1 103 for line in lines: 104 i = i + 1 105 line = line.strip() 106 if len(line) == 0: # allows empty line 107 continue 108 if line[0] == '#': # comment 109 continue 110 try: 111 words = line.split(':') 112 if words[0] == "allcores": 113 allcores = parse_ints(words[1]) 114 config.allcores = allcores 115 elif words[0] == "core_max_freq_khz": 116 pair = words[1].split('=') 117 if len(pair) != 2: 118 raise ValueError("wrong config: {}".format(words[1])) 119 cores = parse_ints(pair[0]) 120 freq = int(pair[1]) 121 for core in cores: 122 config.coreMaxFreqKHz[core] = freq 123 elif words[0] == "default_governor": 124 default_governor = words[1] 125 elif words[0] == "case": 126 current_settings = CpuSettings() 127 current_settings.allcores = allcores 128 current_settings.governor = default_governor 129 config.configs[words[1]] = current_settings 130 elif words[0] == "online": 131 current_settings.onlines = parse_ints(words[1]) 132 elif words[0] == "offline": 133 current_settings.onlines.extend(allcores) 134 offlines = parse_ints(words[1]) 135 for cpu in offlines: 136 current_settings.onlines.remove(cpu) 137 elif words[0] == "cpuset": 138 cpuset_pair = words[1].split('=') 139 if len(cpuset_pair) == 1: 140 current_settings.cpusets[""] = parse_ints(cpuset_pair[0]) 141 else: 142 current_settings.cpusets[cpuset_pair[0]] = parse_ints(cpuset_pair[1]) 143 elif words[0] == "governor": 144 current_settings.governor = words[1] 145 else: 146 raise ValueError("Unknown keyword {}".format(words[0])) 147 except Exception as e: 148 print("Cannot parse line {}: {}".format(i, line)) 149 raise e 150 151 return config 152 153def get_script_dir(): 154 return os.path.dirname(os.path.realpath(sys.argv[0])) 155 156def add_line_with_indentation(strs, msg="", spaces=1): 157 strs.append("\n" + spaces * " " + msg) 158