diff --git a/src/plugins/cpu/index.js b/src/plugins/cpu/index.js index 711b03f28..edfd536ec 100644 --- a/src/plugins/cpu/index.js +++ b/src/plugins/cpu/index.js @@ -16,6 +16,9 @@ import { Text, ManagedTable, colors, + styled, + Panel, + DetailSidebar, } from 'flipper'; type ADBClient = any; @@ -30,6 +33,9 @@ type CPUFrequency = {| scaling_cur_freq: number, scaling_min_freq: number, scaling_max_freq: number, + scaling_available_freqs: Array, + scaling_governor: string, + scaling_available_governors: Array, cpuinfo_max_freq: number, cpuinfo_min_freq: number, |}; @@ -39,6 +45,7 @@ type CPUState = {| cpuCount: number, monitoring: boolean, hardwareInfo: string, + selectedIds: Array, |}; type ShellCallBack = (output: string) => void; @@ -77,8 +84,22 @@ const Columns = { value: 'MAX Frequency', resizable: true, }, + scaling_governor: { + value: 'Scaling Governor', + resizable: true, + }, }; +const Heading = styled('div')({ + fontWeight: 'bold', + fontSize: 13, + display: 'block', + marginBottom: 10, + '&:not(:first-child)': { + marginTop: 20, + }, +}); + // check if str is a number function isNormalInteger(str) { let n = Math.floor(Number(str)); @@ -107,6 +128,7 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { cpuCount: 0, monitoring: false, hardwareInfo: '', + selectedIds: [], }; static supportsDevice(device: Device) { @@ -132,6 +154,9 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { scaling_max_freq: -1, cpuinfo_min_freq: -1, cpuinfo_max_freq: -1, + scaling_available_freqs: [], + scaling_governor: 'N/A', + scaling_available_governors: [], }; } this.setState({ @@ -170,6 +195,44 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { }, 'cat /sys/devices/system/cpu/cpu' + core + '/cpufreq/' + type); }; + updateAvailableFrequencies = (core: number) => { + this.executeShell((output: string) => { + let cpuFreq = this.state.cpuFreq; + let freqs = output.split(' ').map((num: string) => { + return parseInt(num, 10); + }); + cpuFreq[core].scaling_available_freqs = freqs; + let maxFreq = cpuFreq[core].scaling_max_freq; + if (maxFreq > 0 && freqs.indexOf(maxFreq) == -1) { + freqs.push(maxFreq); // always add scaling max to available frequencies + } + this.setState({ + cpuFreq: cpuFreq, + }); + }, 'cat /sys/devices/system/cpu/cpu' + core + '/cpufreq/scaling_available_frequencies'); + }; + + updateCoreGovernor = (core: number) => { + this.executeShell((output: string) => { + let cpuFreq = this.state.cpuFreq; + cpuFreq[core].scaling_governor = output; + this.setState({ + cpuFreq: cpuFreq, + }); + }, 'cat /sys/devices/system/cpu/cpu' + core + '/cpufreq/scaling_governor'); + }; + + readAvailableGovernors = (core: number) => { + this.executeShell((output: string) => { + let cpuFreq = this.state.cpuFreq; + cpuFreq[core].scaling_available_governors = output.split(' '); + + this.setState({ + cpuFreq: cpuFreq, + }); + }, 'cat /sys/devices/system/cpu/cpu' + core + '/cpufreq/scaling_available_governors'); + }; + readCoreFrequency = (core: number) => { let freq = this.state.cpuFreq[core]; if (freq.cpuinfo_max_freq < 0) { @@ -222,10 +285,15 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { if (this.intervalID) { return; } + for (let i = 0; i < this.state.cpuCount; ++i) { + this.readAvailableGovernors(i); + } this.intervalID = setInterval(() => { for (let i = 0; i < this.state.cpuCount; ++i) { this.readCoreFrequency(i); + this.updateCoreGovernor(i); + this.updateAvailableFrequencies(i); // scaling max might change, so we also update this } }, 500); @@ -253,6 +321,8 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { cpuFreq[i].scaling_cur_freq = -1; cpuFreq[i].scaling_min_freq = -1; cpuFreq[i].scaling_max_freq = -1; + cpuFreq[i].scaling_available_freqs = []; + cpuFreq[i].scaling_governor = 'N/A'; // we don't cleanup cpuinfo_min_freq, cpuinfo_max_freq // because usually they are fixed (hardware) } @@ -265,7 +335,8 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { this.cleanup(); }; - buildRow = (freq: CPUFrequency) => { + buildRow = (freq: CPUFrequency, idx: number) => { + let selected = this.state.selectedIds.indexOf(idx) >= 0; let style = {}; if (freq.scaling_cur_freq == -2) { style = { @@ -282,7 +353,8 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { ) { style = { style: { - backgroundColor: colors.redTint, + backgroundColor: selected ? colors.red : colors.redTint, + // better visibility when highlighted color: colors.red, fontWeight: 700, }, @@ -319,18 +391,133 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { cpuinfo_max_freq: { value: {formatFrequency(freq.cpuinfo_max_freq)}, }, + scaling_governor: { + value: {freq.scaling_governor}, + }, }, key: freq.cpu_id, + style, }; }; frequencyRows = (cpuFreqs: Array): TableRows => { - let rows = []; - for (const cpuFreq of cpuFreqs) { - rows.push(this.buildRow(cpuFreq)); + return cpuFreqs.map(this.buildRow); + }; + + buildAvailableFreqList = (freq: CPUFrequency) => { + if (freq.scaling_available_freqs.length == 0) { + return N/A; } - return rows; + let info = freq; + return ( + + {freq.scaling_available_freqs.map((freq, idx) => { + let style = {}; + if ( + freq == info.scaling_cur_freq || + freq == info.scaling_min_freq || + freq == info.scaling_max_freq + ) { + style.fontWeight = 'bold'; + } + return ( + + {formatFrequency(freq)} + {freq == info.scaling_cur_freq && ( + (scaling current) + )} + {freq == info.scaling_min_freq && ( + (scaling min) + )} + {freq == info.scaling_max_freq && ( + (scaling max) + )} +
+
+ ); + })} +
+ ); + }; + + buildAvailableGovList = (freq: CPUFrequency): string => { + if (freq.scaling_available_governors.length == 0) { + return 'N/A'; + } + return freq.scaling_available_governors.join(', '); + }; + + buildSidebarRow = (key: string, val: any) => { + return { + columns: { + key: {value: {key}}, + value: { + value: val, + }, + }, + key: key, + }; + }; + + sidebarRows = (id: number) => { + let availableFreqTitle = 'Scaling Available Frequencies'; + let selected = this.state.cpuFreq[id]; + if (selected.scaling_available_freqs.length > 0) { + availableFreqTitle += + ' (' + selected.scaling_available_freqs.length.toString() + ')'; + } + + let keys = [availableFreqTitle, 'Scaling Available Governors']; + + let vals = [ + this.buildAvailableFreqList(selected), + this.buildAvailableGovList(selected), + ]; + return keys.map((key, idx) => { + return this.buildSidebarRow(key, vals[idx]); + }); + }; + + renderSidebar = () => { + if (this.state.selectedIds.length == 0) { + return null; + } + let id = this.state.selectedIds[0]; + let cols = { + key: { + value: 'key', + resizable: true, + }, + value: { + value: 'value', + resizable: true, + }, + }; + let colSizes = { + key: '35%', + value: 'flex', + }; + return ( + + + CPU_{id} + + + + ); }; render() { @@ -357,7 +544,14 @@ export default class CPUFrequencyTable extends FlipperDevicePlugin { floating={false} zebra={true} rows={this.frequencyRows(this.state.cpuFreq)} + onRowHighlighted={selectedIds => { + this.setState({ + selectedIds: selectedIds, + }); + }} /> + + {this.renderSidebar()} );