blob: dca011cbaa3fedf4599a14e79eda405c5a1de0c7 [file] [log] [blame]
Hugo L'Hostisb605c6c2021-01-06 10:05:01 +00001#!/usr/bin/env python
2
3#memory_footprint.py : Script for sending memory footprint data from the TFM CI
4#to a SQUAD web interface
5#
6#Copyright (c) 2020-2021, Arm Limited. All rights reserved.
7#
8#SPDX-License-Identifier: BSD-3-Clause
9
10
11import argparse
12import os
13import re
14import sys
15import json
16import requests
17import subprocess
18from tfm_ci_pylib import utils
19
20# Arguments/parameters given by CI
21PATH_TO_TFM = sys.argv[1]
22CI_CONFIG = sys.argv[2]
23REFERENCE_CONFIGS = sys.argv[3].split(",")
24SQUAD_TOKEN = sys.argv[4]
25
26# local constant
27SQUAD_BASE_PROJECT_URL = ("https://qa-reports.linaro.org/api/submit/tf/tf-m/")
28
29# This function uses arm_non_eabi_size to get the sizes of a file
30# in the build directory of tfm
31def get_file_size(filename):
32 f_path = os.path.join(PATH_TO_TFM, "build", "bin", filename)
33 if os.path.exists(f_path) :
34 bl2_sizes = utils.arm_non_eabi_size(f_path)[0]
35 return bl2_sizes
36 else :
37 print(f_path + "Not found")
38 return -1
39
40# This function creates a json file containing all the data about
41# memory footprint and sends this data to SQUAD
42def send_file_size(change_id, config_name, bl2_sizes, tfms_sizes):
43 url = SQUAD_BASE_PROJECT_URL + change_id + '/' + config_name
44
45 try:
46 metrics = json.dumps({ "bl2_size" : bl2_sizes["dec"],
47 "bl2_data" : bl2_sizes["data"],
48 "bl2_bss" : bl2_sizes["bss"],
49 "tfms_size" : tfms_sizes["dec"],
50 "tfms_data" : tfms_sizes["data"],
51 "tfms_bss" : tfms_sizes["bss"]})
52 except:
53 return -1
54 headers = {"Auth-Token": SQUAD_TOKEN}
55 data= {"metrics": metrics}
56
57 try:
58 #Sending the data to SQUAD, 40s timeout
59 result = requests.post(url, headers=headers, data=data, timeout=40)
60 except:
61 return -1
62
63 with open(os.path.join(PATH_TO_TFM,
64 "..",
65 "tf-m-ci-scripts",
66 "Memory_footprint",
67 "filesize.json"), "w") as F:
68 #Storing the json file
69 F.write(metrics)
70
71 if not result.ok:
72 print(f"Error submitting to qa-reports: {result.reason}: {result.text}")
73 return -1
74 else :
75 print ("POST request sent to project " + config_name )
76 return 0
77
78#Function used to launch the configs.py script and get the printed output
79def get_config(config_string):
80 script_directory = os.path.join(PATH_TO_TFM, "..", "tf-m-ci-scripts")
81 directory = os.path.abspath(script_directory)
82 cur_dir = os.path.abspath(os.getcwd())
83 os.chdir(directory) # Going to the repo's directory
84
85 cmd = "python3 configs.py " + config_string
86
87 rex = re.compile(r'(?P<config>^[\s\S]*?)((?:TFM_PLATFORM=)'
88 r'(?P<platform>.*)\n)((?:TOOLCHAIN_FILE=)'
89 r'(?P<toolchain>.*)\n)((?:PSA_API=)'
90 r'(?P<psa>.*)\n)((?:ISOLATION_LEVEL=)'
91 r'(?P<isolation_level>.*)\n)((?:TEST_REGRESSION=)'
92 r'(?P<regression>.*)\n)((?:TEST_PSA_API=)'
93 r'(?P<psa_test>.*)\n)((?:CMAKE_BUILD_TYPE=)'
94 r'(?P<build_type>.*)\n)((?:OTP=)'
95 r'(?P<otp>.*)\n)((?:BL2=)'
96 r'(?P<bl2>.*)\n)((?:NS=)'
97 r'(?P<ns>.*)\n)((?:PROFILE=)'
98 r'(?P<profile>.*)\n)((?:PARTITION_PS=)'
99 r'(?P<partition_ps>.*)?)', re.MULTILINE)
100
101 r, e = subprocess.Popen(cmd,
102 shell=True,
103 stdout=subprocess.PIPE,
104 stderr=subprocess.PIPE).communicate()
105 if e:
106 print("Error", e)
107 return -1
108 else:
109 try:
110 txt_body = r.decode('ascii')
111 except UnicodeDecodeError as E:
112 txt_body = r.decode('utf-8')
113
114 m = rex.search(txt_body)
115 match_dict = m.groupdict()
116
117 match_dict = {k: simple_filter(v) for k, v in match_dict.items()}
118
119 os.chdir(cur_dir)
120
121 return match_dict
122
123def simple_filter(A):
124 try:
125 a = A.lower()
126 if a in ["true", "on"]:
127 return True
128 elif a in ["false", "off"]:
129 return False
130 else:
131 return A
132 except Exception:
133 return A
134
135# This funcion manipulates the format of the config given
136# as entry to extract the name of the configuration used
137def identify_config(ci_config):
138 arguments = get_config(CI_CONFIG)
139 name_config = "Unknown"
140 try :
141 if (arguments["psa"] and arguments["isolation_level"] == "1" and
142 not arguments["regression"] and not arguments["psa_test"] and
143 arguments["build_type"] == "Release" and not arguments["otp"] and
144 arguments["bl2"] and arguments["ns"] and
145 arguments["profile"] == "N.A" and arguments["partition_ps"]):
146 name_config = "CoreIPC"
147 elif (not arguments["psa"] and arguments["isolation_level"] == "1" and
148 not arguments["regression"] and not arguments["psa_test"] and
149 arguments["build_type"] == "Release" and not arguments["otp"] and
150 arguments["bl2"] and arguments["ns"] and
151 arguments["profile"] == "N.A" and arguments["partition_ps"]):
152 name_config = "Default"
153 elif (arguments["psa"] and arguments["isolation_level"] == "2" and
154 not arguments["regression"] and not arguments["psa_test"] and
155 arguments["build_type"] == "Release" and not arguments["otp"] and
156 arguments["bl2"] and arguments["ns"] and
157 arguments["profile"] == "N.A" and arguments["partition_ps"]):
158 name_config = "CoreIPCTfmLevel2"
159 elif (not arguments["psa"] and arguments["isolation_level"] == "1" and
160 not arguments["regression"] and not arguments["psa_test"] and
161 arguments["build_type"] == "Release" and not arguments["otp"] and
162 arguments["bl2"] and arguments["ns"] and
163 arguments["profile"] == "SMALL" and arguments["partition_ps"]):
164 name_config = "DefaultProfileS"
165 elif (not arguments["psa"] and arguments["isolation_level"] == "2" and
166 not arguments["regression"] and not arguments["psa_test"] and
167 arguments["build_type"] == "Release" and not arguments["otp"] and
168 arguments["bl2"] and arguments["ns"] and
169 arguments["profile"] == "MEDIUM" and arguments["partition_ps"]):
170 name_config = "DefaultProfileM"
171 ret = [arguments["platform"],arguments["toolchain"], name_config]
172 except:
173 ret = ["Unknown", "Unknown", "Unknown"]
174 return ret
175
176# Function based on get_local_git_info() from utils, getting change id for the tfm repo
177def get_change_id(directory):
178 directory = os.path.abspath(directory)
179 cur_dir = os.path.abspath(os.getcwd())
180 os.chdir(directory) # Going to the repo's directory
181 cmd = "git log HEAD -n 1 --pretty=format:'%b'"
182
183 git_info_rex = re.compile(r'(?P<body>^[\s\S]*?)((?:Change-Id:\s)'
184 r'(?P<change_id>.*)\n?)', re.MULTILINE)
185
186 r, e = subprocess.Popen(cmd,
187 shell=True,
188 stdout=subprocess.PIPE,
189 stderr=subprocess.PIPE).communicate()
190 if e:
191 print("Error", e)
192 return
193 else:
194 try:
195 txt_body = r.decode('ascii')
196 except UnicodeDecodeError as E:
197 txt_body = r.decode('utf-8')
198 result = txt_body.rstrip()
199
200 change_id = git_info_rex.search(result).groupdict()["change_id"].strip()
201 os.chdir(cur_dir) #Going back to the initial directory
202 return change_id
203
204if __name__ == "__main__":
205 for i in range(len(REFERENCE_CONFIGS)):
206 REFERENCE_CONFIGS[i] = REFERENCE_CONFIGS[i].strip().lower()
207 config = identify_config(CI_CONFIG)
208 if (config[2].lower() in REFERENCE_CONFIGS
209 and config[0] == "mps2/an521"
210 and config[1] == "toolchain_GNUARM.cmake"):
211 # Pushing data for AN521 and GNUARM
212 print("Configuration " + config[2] + " is a reference")
213 try :
214 change_id = get_change_id(PATH_TO_TFM)
215 except :
216 change_id = -1
217 bl2_sizes = get_file_size("bl2.axf")
218 tfms_sizes = get_file_size("tfm_s.axf")
219 if (bl2_sizes != -1 and change_id != -1) :
220 send_file_size(change_id, config[2], bl2_sizes, tfms_sizes)
221 else :
222 #Directory or file weren't found
223 if change_id == -1 :
224 print("Error : trusted-firmware-m repo not found")
225 else :
226 print("Error : file not found")