Dosyalar
rocm-systems/tools/generator.py
T
Joe Narlo b5887c2f05 SWDEV-487604 [AMD SMI][Unified Header] integration_test.py is failing with unified header
The script generator.py was not handling all of the anonymous and unnamed structures.
Logic was added to correct the errors seen in the script amdsmi_wrapper.py

Removed adding _ to structure definitions

Change-Id: I51958d23b3da40ec67e883e13dc74feaeaf1d58e
Signed-off-by: Joe Narlo <Joseph.Narlo@amd.com>
2024-10-15 16:22:02 -04:00

349 satır
14 KiB
Python

#
# Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
import os
import argparse
import tempfile
import shutil
import platform
from subprocess import run, PIPE
from ctypeslib.clang2py import main as clangToPy
HEADER = \
"""
#
# Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
import os
"""
def parseArgument():
parser = argparse.ArgumentParser(description="parse input arguments")
parser.add_argument('-o','--output', type=str, required=True,
help='The output file name')
parser.add_argument('-i','--input', type=str, required=True,
help='The input file name')
parser.add_argument('-l', '--library', type=str, required=True,
help='Loading dynamic link libraries')
parser.add_argument('-e', '--extra-args', type=str, required=False,
help='Parse extra arguments to clang')
args = vars(parser.parse_args())
return args['output'], args['input'], args['library'], args['extra_args']
def replace_line(full_path_file_name, string_to_replace, new_string):
"""
Replaces a specific string in a file with a new string.
Args:
full_path_file_name (str): The full path of the file to modify.
string_to_replace (str): The string to be replaced.
new_string (str): The new string to replace the old string with.
Returns:
None
"""
fh, abs_path = tempfile.mkstemp()
with os.fdopen(fh, 'w') as new_file:
with open(full_path_file_name, 'r+', encoding='UTF-8') as old_file:
for line in old_file:
new_file.write(line.replace(string_to_replace, new_string))
shutil.copymode(full_path_file_name, abs_path)
os.remove(full_path_file_name)
shutil.move(abs_path, full_path_file_name)
def write_header(full_path_file_name):
fh, abs_path = tempfile.mkstemp()
with os.fdopen(fh, 'w') as new_file:
new_file.write(HEADER)
with open(full_path_file_name, 'r+', encoding='UTF-8') as old_file:
for line in old_file:
new_file.write(line)
shutil.copymode(full_path_file_name, abs_path)
os.remove(full_path_file_name)
shutil.move(abs_path, full_path_file_name)
def write_file(full_path_file_name, contents):
fh, abs_path = tempfile.mkstemp()
with os.fdopen(fh, 'w') as new_file:
for line in contents:
new_file.write(f'{line}\n')
shutil.copymode(full_path_file_name, abs_path)
os.remove(full_path_file_name)
shutil.move(abs_path, full_path_file_name)
def find_replacement(search_str1, search_str2, line):
pos1 = line.find(search_str1)
if pos1 < 0:
return ''
if len(search_str2):
pos2 = line.find(search_str2, pos1)
if pos2 < 0:
return ''
else:
pos2 = len(line) - 1
return line[pos1:pos2+1]
def find_line_num(search_str, line):
pos1 = line.find(search_str)
if pos1 < 0:
return 0
items = line[pos1:].split(':')
if len(items) < 2:
return 0
line_num = items[1].strip()
if not line_num.isdigit():
return 0
line_num = int(line_num)
return (line_num)
def main():
output_file, input_file, library, clang_extra_args = parseArgument()
# make args string easy to append
if clang_extra_args is None:
clang_extra_args = ""
else:
clang_extra_args = " " + clang_extra_args
library_name = os.path.basename(library)
clang_include_dir = \
run(["clang", "--print-resource-dir"], stdout=PIPE, stderr=PIPE, encoding="utf-8").stdout.strip()
os_platform = platform.system()
if os_platform == "Windows":
clang_include_dir += "\\include"
if "Program Files(x86)" in clang_include_dir:
clang_include_dir = clang_include_dir.replace("Program Files(x86)", "Progra~2")
elif "Program Files" in clang_include_dir:
clang_include_dir = clang_include_dir.replace("Program Files", "Progra~1")
arguments = [input_file, "-o", output_file]
line_to_replace = "_libraries['FIXME_STUB'] = FunctionFactoryStub() # ctypes.CDLL('FIXME_STUB')"
new_line = "_libraries['FIXME_STUB'] = ctypes.CDLL('{}')".format(library_name)
elif os_platform == "Linux":
clang_include_dir += "/include"
arguments = [input_file, "-o", output_file, "-l", library]
library_path = os.path.join(os.path.dirname(__file__), library)
line_to_replace = "_libraries['{}'] = ctypes.CDLL('{}')".format(library_name, library_path)
new_line = f"""from pathlib import Path
libamd_smi_parent_dir = Path(__file__).resolve().parent / "{library_name}"
libamd_smi_cwd = Path.cwd() / "{library_name}"
try:
if libamd_smi_parent_dir.is_file():
# try to fall back to parent directory
_libraries['{library_name}'] = ctypes.CDLL(libamd_smi_parent_dir)
else:
# lastly - search in current working directory
_libraries['{library_name}'] = ctypes.CDLL(libamd_smi_cwd)
except OSError as error:
print(error)
print("Unable to find amdsmi library try installing amd-smi-lib from your package manager")"""
else:
print("Unknown operating system. It is only supporing Linux and Windows.")
return
arguments.append("--clang-args=-I" + clang_include_dir + clang_extra_args)
clangToPy(arguments)
replace_line(output_file, line_to_replace, new_line)
write_header(output_file)
# Custom handling for <anonymous|unnamed> struct in Linux
if os_platform == "Linux":
with open(input_file, 'r') as fin:
input_file_contents = fin.read()
input_file_array = input_file_contents.split('\n')
with open(output_file, 'r') as fin:
output_file_contents = fin.read()
output_file_array = output_file_contents.split('\n')
# Find all unamed occurences in the output_file
struct_name_dict = {}
for index, line in enumerate(output_file_array):
if 'amdsmi.h:' in line:
# Handling "struct_struct (<anonymous:unnamed> at amdsmi.h:<num>:<num>)"
if 'anonymous' in line or 'unnamed' in line:
search_name = 'unnamed'
if 'anonymous' in line:
search_name = 'anonymous'
# Find the amdsmi.h line number for this instance
# Example 1:
# class struct_struct (anonymous at amdsmi.h:370:9)(Structure):
# line_num = 370
# Example 2:
# class struct_struct (unnamed at amdsmi.h:782:9)(Structure):
# line_num = 782
line_num = find_line_num(search_name, line)
if line_num == 0:
print(f'Error: {index+1}: Could determine amdsmi.h line number in {line}, skipping replacement')
continue
# Using in amdsmi.h starting at the line_num to find the structure name
# Search the following lines for "open curly bracket" that has a name
# Example 1:
# 369: typedef union {
# 370: struct {
# 371: uint64_t function_number : 3;
# 375: };
# 377: } amdsmi_bdf_t;
# struct_name = amdsmi_bdf_t
# Example 2:
# 782: struct {
# 783: uint64_t gfx;
# 786: } engine_usage;
# struct_name = engine_usage
struct_name = ''
for i in range(1, 50):
input_line = input_file_array[line_num + i]
# { matches close curly brackets on next line
if '}' in input_line:
struct_name = input_line.strip()[1:-1].strip()
if len(struct_name):
struct_name = struct_name.split('[')[0] # ]
break
if not len(struct_name):
print(f'Error: {index+1}: Could not find struct name using line number {line_num}, skipping replacement')
continue
# Generate the replacement for this line
# Example:
# class struct_struct (unnamed at amdsmi.h:782:9)(Structure):
# becomes
# class struct_engine_usage(Structure):
str_replace = find_replacement('struct_struct', ')', line)
if len(str_replace) > 0:
str_with = f'struct_{struct_name}'
else:
# Example
# (unnamed at amdsmi.h:787:9)', 'uint32_t', 'uint64_t', 'uint8_t',
# becomes
# 'struct_memory_usage', 'uint32_t', 'uint64_t', 'uint8_t',
str_replace = find_replacement(f'({search_name}', ')', line)
if len(str_replace) == 0:
print(f'Error: {index+1}: Could not find structure name in {line}, skipping replacement')
continue
str_with = f"'struct_{struct_name}"
# Save the line number and struct_name association for possible additional replacements
if line_num not in struct_name_dict:
struct_name_dict[line_num] = struct_name
# Do the replace
new_line = line.replace(str_replace, str_with)
# Look for special replacements that has the struct_name
# Example
# ('_0', struct_struct (anonymous at amdsmi.h:370:9)),
# becomes
# ('struct_amdsmi_bdf_t', struct_amdsmi_bdf_t),
if '_0' in new_line:
new_line = new_line.replace('_0', f'struct_{struct_name}')
# Look for special replacements that has an amdsmi.h:
# Example
# amdsmi.h:370:9)', 'uint8_t',
# becomes
# 'uint8_t,
if 'amdsmi.h:' in new_line:
str_replace = find_replacement('amdsmi.h:', ',', line)
if len(str_replace) > 0:
new_line = new_line.replace(str_replace, '')
# Save the replaced line into the array
output_file_array[index] = new_line
# Look for special replacements
new_line = output_file_array[index]
# Example
# union_amdsmi_bdf_t._anonymous_ = ('_0',)
# becomes
#
if '_anonymous_' in new_line:
new_line = ''
output_file_array[index] = new_line
# Example
# 'struct_pcie_static_', 'struct_struct (anonymous at
# becomes
# 'struct_pcie_static_',
name = ", 'struct_struct"
if name in new_line:
str_replace = find_replacement(name, '', new_line)
if len(str_replace) > 0:
new_line = new_line.replace(str_replace, ',')
output_file_array[index] = new_line
# Example
# amdsmi_get_utilization_count.argtypes = [amdsmi_processor_handle, struct_amdsmi_utilization_counter_t * 0, uint32_t, ctypes.POINTER(ctypes.c_uint64)]
# becomes
# amdsmi_get_utilization_count.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_utilization_counter_t), uint32_t, ctypes.POINTER(ctypes.c_uint64)]
name = "amdsmi_get_utilization_count.argtypes"
if name in new_line:
str_replace = find_replacement(name, '', new_line)
if len(str_replace) > 0:
str_with = 'amdsmi_get_utilization_count.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_utilization_counter_t), uint32_t, ctypes.POINTER(ctypes.c_uint64)]'
new_line = new_line.replace(str_replace, str_with)
output_file_array[index] = new_line
write_file(output_file, output_file_array)
if __name__ == "__main__":
main()