• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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