1# Copyright 2015-2017 ARM Limited 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# 15 16"""Process the output of the cpu_cooling devices in the current 17directory's trace.dat""" 18 19import pandas as pd 20 21from trappy.base import Base 22from trappy.dynamic import register_ftrace_parser 23 24def pivot_with_labels(dfr, data_col_name, new_col_name, mapping_label): 25 """Pivot a :mod:`pandas.DataFrame` row into columns 26 27 :param dfr: The :mod:`pandas.DataFrame` to operate on. 28 29 :param data_col_name: The name of the column in the :mod:`pandas.DataFrame` 30 which contains the values. 31 32 :param new_col_name: The name of the column in the :mod:`pandas.DataFrame` that will 33 become the new columns. 34 35 :param mapping_label: A dictionary whose keys are the values in 36 new_col_name and whose values are their 37 corresponding name in the :mod:`pandas.DataFrame` to be returned. 38 39 :type dfr: :mod:`pandas.DataFrame` 40 :type data_col_name: str 41 :type new_col_name: str 42 :type mapping_label: dict 43 44 Example: 45 46 >>> dfr_in = pd.DataFrame({'cpus': ["000000f0", 47 >>> "0000000f", 48 >>> "000000f0", 49 >>> "0000000f" 50 >>> ], 51 >>> 'freq': [1, 3, 2, 6]}) 52 >>> dfr_in 53 cpus freq 54 0 000000f0 1 55 1 0000000f 3 56 2 000000f0 2 57 3 0000000f 6 58 59 >>> map_label = {"000000f0": "A15", "0000000f": "A7"} 60 >>> power.pivot_with_labels(dfr_in, "freq", "cpus", map_label) 61 A15 A7 62 0 1 NaN 63 1 1 3 64 2 2 3 65 3 2 6 66 67 """ 68 69 # There has to be a more "pandas" way of doing this. 70 71 col_set = set(dfr[new_col_name]) 72 73 ret_series = {} 74 for col in col_set: 75 try: 76 label = mapping_label[col] 77 except KeyError: 78 available_keys = ", ".join(mapping_label.keys()) 79 error_str = '"{}" not found, available keys: {}'.format(col, 80 available_keys) 81 raise KeyError(error_str) 82 data = dfr[dfr[new_col_name] == col][data_col_name] 83 84 ret_series[label] = data 85 86 return pd.DataFrame(ret_series).fillna(method="pad") 87 88def num_cpus_in_mask(mask): 89 """Return the number of cpus in a cpumask""" 90 91 mask = mask.replace(",", "") 92 value = int(mask, 16) 93 94 return bin(value).count("1") 95 96class CpuOutPower(Base): 97 """Process the cpufreq cooling power actor data in a ftrace dump""" 98 99 unique_word = "thermal_power_cpu_limit" 100 """The unique word that will be matched in a trace line""" 101 102 name = "cpu_out_power" 103 """The name of the :mod:`pandas.DataFrame` member that will be created in a 104 :mod:`trappy.ftrace.FTrace` object""" 105 106 pivot = "cpus" 107 """The Pivot along which the data is orthogonal""" 108 109 def get_all_freqs(self, mapping_label): 110 """Get a :mod:`pandas.DataFrame` with the maximum frequencies allowed by the governor 111 112 :param mapping_label: A dictionary that maps cpumasks to name 113 of the cpu. 114 :type mapping_label: dict 115 116 :return: freqs are in MHz 117 """ 118 119 dfr = self.data_frame 120 121 return pivot_with_labels(dfr, "freq", "cpus", mapping_label) / 1000 122 123register_ftrace_parser(CpuOutPower, "thermal") 124 125class CpuInPower(Base): 126 """Process the cpufreq cooling power actor data in a ftrace dump 127 """ 128 129 unique_word = "thermal_power_cpu_get" 130 """The unique word that will be matched in a trace line""" 131 132 name = "cpu_in_power" 133 """The name of the :mod:`pandas.DataFrame` member that will be created in a 134 :mod:`trappy.ftrace.FTrace` object""" 135 136 pivot = "cpus" 137 """The Pivot along which the data is orthogonal""" 138 139 def _get_load_series(self): 140 """get a :mod:`pandas.Series` with the aggregated load""" 141 142 dfr = self.data_frame 143 load_cols = [s for s in dfr.columns if s.startswith("load")] 144 145 load_series = dfr[load_cols[0]].copy() 146 for col in load_cols[1:]: 147 load_series += dfr[col] 148 149 return load_series 150 151 def get_load_data(self, mapping_label): 152 """Return :mod:`pandas.DataFrame` suitable for plot_load() 153 154 :param mapping_label: A Dictionary mapping cluster cpumasks to labels 155 :type mapping_label: dict 156 """ 157 158 dfr = self.data_frame 159 load_series = self._get_load_series() 160 load_dfr = pd.DataFrame({"cpus": dfr["cpus"], "load": load_series}) 161 162 return pivot_with_labels(load_dfr, "load", "cpus", mapping_label) 163 164 def get_normalized_load_data(self, mapping_label): 165 """Return a :mod:`pandas.DataFrame` for plotting normalized load data 166 167 :param mapping_label: should be a dictionary mapping cluster cpumasks 168 to labels 169 :type mapping_label: dict 170 """ 171 172 dfr = self.data_frame 173 load_series = self._get_load_series() 174 175 load_series *= dfr['freq'] 176 for cpumask in mapping_label: 177 num_cpus = num_cpus_in_mask(cpumask) 178 idx = dfr["cpus"] == cpumask 179 max_freq = max(dfr[idx]["freq"]) 180 load_series[idx] = load_series[idx] / (max_freq * num_cpus) 181 182 load_dfr = pd.DataFrame({"cpus": dfr["cpus"], "load": load_series}) 183 184 return pivot_with_labels(load_dfr, "load", "cpus", mapping_label) 185 186 def get_all_freqs(self, mapping_label): 187 """get a :mod:`pandas.DataFrame` with the "in" frequencies as seen by the governor 188 189 .. note:: 190 191 Frequencies are in MHz 192 """ 193 194 dfr = self.data_frame 195 196 return pivot_with_labels(dfr, "freq", "cpus", mapping_label) / 1000 197 198register_ftrace_parser(CpuInPower, "thermal") 199