1#!/usr/bin/env python3 2 3# 4# Copyright 2024, The Android Open Source Project 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19"""This program validates pixel powerhint configuration strings 20against a predefined list of fields. 21""" 22 23from __future__ import print_function 24 25import argparse 26import errno 27import json 28import os 29import shutil 30import subprocess 31import sys 32import gitlint.git as git 33 34from pixel_config_checker import PixelJSONFieldNameChecker 35 36def powerhint_check_actions_and_nodes(powerhint_json_files): 37 """Preupload check for powerhint actions and nodes. 38 39 This function checks that all powerhint actions on nodes 40 and other actions (DoHint, EndHint, etc..) are valid and 41 nodes exist. It also validates that values specified in 42 actions are supported values in nodes. It also checks if 43 nodes are double declared. 44 45 Args: Map of powerhint json file names to actions. 46 47 Returns: 48 Status, Error Message. 49 """ 50 51 for file_path, powerhint in powerhint_json_files.items(): 52 nodes_dict = dict() 53 action_names = set() 54 55 # Create reference Nodes and Actions 56 for node in powerhint["Nodes"]: 57 if node["Name"] in nodes_dict: 58 return False, file_path + ": repeated node " + node["Name"] 59 nodes_dict[node["Name"]] = node["Values"] 60 61 for action in powerhint["Actions"]: 62 action_names.add(action["PowerHint"]) 63 64 for action in powerhint["Actions"]: 65 if "Type" in action: 66 if action["Value"] not in action_names: 67 return False, file_path + ": Action " + action["PowerHint"] + \ 68 ": unknown Hint " + action["Value"] 69 70 if "Node" in action: 71 if action["Node"] not in nodes_dict.keys(): 72 return False, file_path + ": Action " + action["PowerHint"] + \ 73 ": unknown Node " + action["Node"] 74 75 if action["Value"] not in nodes_dict[action["Node"]]: 76 return False, file_path + ": Action " + action["PowerHint"] + \ 77 ": Node " + action["Node"] + " unknown value " + action["Value"] 78 79 return True, "" # Return True if all actions are valid 80 81 82 83def get_powerhint_modified_files(commit): 84 """Getter for finding which powerhint json files were modified 85 in the commit. 86 87 Args: Commit sha 88 89 Returns: 90 modified_files: List of powerhint config files modified if any. 91 """ 92 root = git.repository_root() 93 modified_files = git.modified_files(root, True, commit) 94 modified_files = {f: modified_files[f] for f 95 in modified_files if f.endswith('.json') and 'powerhint' in f} 96 97 return modified_files 98 99def main(args=None): 100 """Main function for checking powerhint configs. 101 102 Args: 103 commit: The change commit's SHA. 104 field_names: The path to known field names. 105 106 Returns: 107 Exits with error if unsuccessful. 108 """ 109 110 # Mapping of form (json path, json object) 111 json_files = dict() 112 113 # Load arguments provided from repo hooks. 114 parser = argparse.ArgumentParser() 115 parser.add_argument('--commit', '-c') 116 parser.add_argument('--field_names', '-l') 117 args = parser.parse_args() 118 if not args.commit: 119 return "Invalid commit provided" 120 121 if not args.field_names: 122 return "No field names path provided" 123 124 if not git.repository_root(): 125 return "Not inside a git repository" 126 127 # Gets modified and added json files in current commit. 128 powerhint_check_file_paths = get_powerhint_modified_files(args.commit) 129 if not list(powerhint_check_file_paths.keys()): 130 return 0 131 132 # Populate and validate (json path, json object) maps to test. 133 for file_name in powerhint_check_file_paths.keys(): 134 rel_path = os.path.relpath(file_name) 135 content = subprocess.check_output( 136 ["git", "show", args.commit + ":" + rel_path]) 137 try: 138 json_file = json.loads(content) 139 json_files[rel_path] = json_file 140 except ValueError as e: 141 return "Malformed JSON file " + rel_path + " with message "+ str(e) 142 143 # Instantiates the common config checker and runs tests on config. 144 checker = PixelJSONFieldNameChecker(json_files, args.field_names) 145 success, message = checker.check_json_field_names() 146 if not success: 147 return "powerhint JSON field name check error: " + message 148 149 success, message = powerhint_check_actions_and_nodes(json_files) 150 if not success: 151 return "powerhint JSON field name check error: " + message 152 153if __name__ == '__main__': 154 ret = main() 155 if ret: 156 print(ret) 157 print("----------------------------------------------------") 158 print("| !! Please see go/pixel-perf-thermal-preupload !! |") 159 print("----------------------------------------------------") 160 sys.exit(1) 161 162