SWDEV-281658 - Preserve the callback IDs enum ordering

This change addresses the rocprofiler and HIP backward compatibility
issues. Before this patch, each time the hip_prof_str.h header was
generated, the ordering of the callbacks IDs changed, causing
incompatibilities between tools compiled with the old header and
runtimes compiled with the new headers (or vice versa).

To make the API callback IDs stable, the previous version of the header
is read to extract the enum values so that the same values can be
assigned in the new header.

Also, to make diffing different versions of the hip_prof_str.h easier
to read, all other sections (types, macros, helper functions) are now
alphabetically ordered.

If an update to the checked-in hip_prof_str.h file is required, the
cmake build is aborted and a message printed on stderr. The build will
not be successful until the checked-in hip_prof_str.h and the generated
hip_prof_str.h match.

Change-Id: I38b920e601185f7365a76a6584df91a7e8a11798
Этот коммит содержится в:
Christophe Paquot
2021-06-18 22:53:35 -07:00
коммит произвёл Christophe Paquot
родитель 688288c2e3
Коммит d208afcb36
4 изменённых файлов: 5729 добавлений и 44 удалений
Разница между файлами не показана из-за своего большого размера Загрузить разницу
+3 -2
Просмотреть файл
@@ -251,6 +251,7 @@ endif()
#############################
# Generate profiling API macros/structures header
set(PROF_API_STR "${PROJECT_BINARY_DIR}/include/hip/amd_detail/hip_prof_str.h")
set(PROF_API_STR_IN "${CMAKE_SOURCE_DIR}/include/hip/amd_detail/hip_prof_str.h")
set(PROF_API_HDR "${HIP_COMMON_INCLUDE_DIR}/hip/hip_runtime_api.h")
set(PROF_API_SRC "${CMAKE_CURRENT_SOURCE_DIR}")
set(PROF_API_GEN "${CMAKE_CURRENT_SOURCE_DIR}/hip_prof_gen.py")
@@ -258,8 +259,8 @@ set(PROF_API_LOG "${PROJECT_BINARY_DIR}/hip_prof_gen.log.txt")
find_package(PythonInterp REQUIRED)
add_custom_command(OUTPUT ${PROF_API_STR}
COMMAND ${PYTHON_EXECUTABLE} ${PROF_API_GEN} -v -t --priv ${PROF_API_HDR} ${PROF_API_SRC} ${PROF_API_STR}
DEPENDS ${PROF_API_HDR} ${PROF_API_GEN}
COMMAND ${PYTHON_EXECUTABLE} ${PROF_API_GEN} -v -t --priv ${PROF_API_HDR} ${PROF_API_SRC} ${PROF_API_STR_IN} ${PROF_API_STR}
DEPENDS ${PROF_API_STR_IN} ${PROF_API_HDR} ${PROF_API_GEN}
COMMENT "Generating profiling primitives: ${PROF_API_STR}")
add_custom_target(gen-prof-api-str-header ALL
+5 -5
Просмотреть файл
@@ -65,7 +65,7 @@ class api_callbacks_table_t {
};
struct hip_cb_table_t {
hip_cb_table_entry_t arr[HIP_API_ID_NUMBER] = {};
hip_cb_table_entry_t arr[HIP_API_ID_LAST + 1] = {};
};
api_callbacks_table_t() = default;
@@ -74,7 +74,7 @@ class api_callbacks_table_t {
std::lock_guard<mutex_t> lock(mutex_);
bool ret = true;
if (id < HIP_API_ID_NUMBER) {
if (id >= HIP_API_ID_FIRST && id <= HIP_API_ID_LAST) {
cb_sync(id);
/*
'fun != nullptr' indicates it is activity register call,
@@ -112,7 +112,7 @@ class api_callbacks_table_t {
std::lock_guard<mutex_t> lock(mutex_);
bool ret = true;
if (id < HIP_API_ID_NUMBER) {
if (id >= HIP_API_ID_FIRST && id <= HIP_API_ID_LAST) {
cb_sync(id);
callbacks_table_.arr[id].fun = fun;
callbacks_table_.arr[id].arg = arg;
@@ -192,7 +192,7 @@ class api_callbacks_spawner_t {
{
if (!was_enabled_on_construction_) return;
if (cid_ >= HIP_API_ID_NUMBER) {
if (cid_ < HIP_API_ID_FIRST || cid_ > HIP_API_ID_LAST) {
fprintf(stderr, "HIP %s bad id %d\n", __FUNCTION__, cid_);
abort();
}
@@ -244,7 +244,7 @@ class api_callbacks_spawner_t {
};
template <>
class api_callbacks_spawner_t<HIP_API_ID_NUMBER> {
class api_callbacks_spawner_t<HIP_API_ID_NONE> {
public:
api_callbacks_spawner_t() {}
void call() {}
+91 -37
Просмотреть файл
@@ -21,6 +21,8 @@
# THE SOFTWARE.
import os, sys, re
import CppHeaderParser
import filecmp
PROF_HEADER = "hip_prof_str.h"
OUTPUT = PROF_HEADER
@@ -375,49 +377,61 @@ def parse_src(api_map, src_path, src_patt, out):
#############################################################
# Generating profiling primitives header
# api_map - public API map [<api name>] => [(type, name), ...]
# callback_ids - public API callback IDs list (name, callback_id)
# opts_map - opts map [<api name>] => [opt0, opt1, ...]
def generate_prof_header(f, api_map, opts_map):
def generate_prof_header(f, api_map, callback_ids, opts_map):
# Private API list
priv_lst = []
f.write('// automatically generated sources\n')
f.write('// Generated file. DO NOT EDIT.\n')
f.write('//\n')
f.write('// This file is automatically generated by the ' + os.path.basename(__file__) + ' script.\n')
f.write('// If changes are required, run the script and commit the updated file.\n\n')
f.write('#ifndef _HIP_PROF_STR_H\n');
f.write('#define _HIP_PROF_STR_H\n');
f.write('#define HIP_PROF_VER 1\n')
# Generating dummy macro for non-public API
f.write('\n// Dummy API primitives\n')
f.write('#define INIT_NONE_CB_ARGS_DATA(cb_data) {};\n')
for name in opts_map:
# Check for non-public API
for name in sorted(opts_map.keys()):
if not name in api_map:
opts_lst = opts_map[name]
if len(opts_lst) != 0:
fatal("bad dummy API \"" + name + "\", args: " + str(opts_lst))
f.write('#define INIT_'+ name + '_CB_ARGS_DATA(cb_data) {};\n')
priv_lst.append(name)
for name in priv_lst:
message("Private: " + name)
message("Private: " + name)
# Generating the callbacks ID enumaration
f.write('\n// HIP API callbacks ID enumaration\n')
f.write('\n// HIP API callbacks ID enumeration\n')
f.write('enum hip_api_id_t {\n')
cb_id = 0
for name in api_map.keys():
f.write(' HIP_API_ID_' + name + ' = ' + str(cb_id) + ',\n')
cb_id += 1
f.write(' HIP_API_ID_NUMBER = ' + str(cb_id) + ',\n')
f.write(' HIP_API_ID_NONE = 0,\n')
f.write(' HIP_API_ID_FIRST = 1,\n')
cb_id_map = {}
last_cb_id = 0
for name, cb_id in callback_ids:
if not name in api_map:
f.write(' HIP_API_ID_RESERVED_' + str(cb_id) + ' = ' + str(cb_id) + ',\n')
else:
f.write(' HIP_API_ID_' + name + ' = ' + str(cb_id) + ',\n')
cb_id_map[name] = cb_id
if cb_id > last_cb_id: last_cb_id = cb_id
for name in sorted(api_map.keys()):
if not name in cb_id_map:
last_cb_id += 1
f.write(' HIP_API_ID_' + name + ' = ' + str(last_cb_id) + ',\n')
f.write(' HIP_API_ID_LAST = ' + str(last_cb_id) + ',\n')
f.write('\n')
f.write(' HIP_API_ID_NONE = HIP_API_ID_NUMBER,\n')
for name in priv_lst:
f.write(' HIP_API_ID_' + name + ' = HIP_API_ID_NUMBER,\n')
for name in sorted(priv_lst):
f.write(' HIP_API_ID_' + name + ' = HIP_API_ID_NONE,\n')
f.write('};\n')
# Generating the method to return API name by ID
f.write('\n// Return HIP API string by given ID\n')
f.write('\n// Return the HIP API string for a given callback ID\n')
f.write('static inline const char* hip_api_name(const uint32_t id) {\n')
f.write(' switch(id) {\n')
for name in api_map.keys():
for name in sorted(api_map.keys()):
f.write(' case HIP_API_ID_' + name + ': return "' + name + '";\n')
f.write(' };\n')
f.write(' return "unknown";\n')
@@ -426,22 +440,23 @@ def generate_prof_header(f, api_map, opts_map):
# Generating the method for querying API ID by name
f.write('\n')
f.write('#include <string.h>\n');
f.write('// Return HIP API ID by given name\n')
f.write('// Return the HIP API callback ID for a given name\n')
f.write('static inline uint32_t hipApiIdByName(const char* name) {\n')
for name, args in api_map.items():
for name in sorted(api_map.keys()):
f.write(' if (strcmp("' + name + '", name) == 0) return HIP_API_ID_' + name + ';\n')
f.write(' return HIP_API_ID_NUMBER;\n')
f.write(' return HIP_API_ID_NONE;\n')
f.write('}\n')
# Generating the callbacks data structure
f.write('\n// HIP API callbacks data structure\n')
f.write('\n// HIP API callbacks data structures\n')
f.write(
'typedef struct hip_api_data_s {\n' +
' uint64_t correlation_id;\n' +
' uint32_t phase;\n' +
' union {\n'
)
for name, args in api_map.items():
for name in sorted(api_map.keys()):
args = api_map[name]
if len(args) != 0:
f.write(' struct {\n')
for arg_tuple in args:
@@ -462,7 +477,8 @@ def generate_prof_header(f, api_map, opts_map):
# Generating the callbacks args data filling macros
f.write('\n// HIP API callbacks args data filling macros\n')
for name, args in api_map.items():
for name in sorted(api_map.keys()):
args = api_map[name]
f.write('// ' + name + str(args) + '\n')
f.write('#define INIT_' + name + '_CB_ARGS_DATA(cb_data) { \\\n')
if name in opts_map:
@@ -485,13 +501,20 @@ def generate_prof_header(f, api_map, opts_map):
f.write('};\n')
f.write('#define INIT_CB_ARGS_DATA(cb_id, cb_data) INIT_##cb_id##_CB_ARGS_DATA(cb_data)\n')
f.write('#if HIP_PROF_HIP_API_STRING\n')
# Generating macro for non-public API
f.write('\n// Macros for non-public API primitives\n')
for name in sorted(priv_lst):
f.write('// ' + name + '()\n')
f.write('#define INIT_'+ name + '_CB_ARGS_DATA(cb_data) {};\n')
f.write('\n#define INIT_NONE_CB_ARGS_DATA(cb_data) {};\n')
f.write('\n#if HIP_PROF_HIP_API_STRING\n')
# Generating the method for the API args filling
f.write('\n')
f.write('// HIP API args filling method\n')
f.write('// HIP API args filling helper\n')
f.write('static inline void hipApiArgsInit(hip_api_id_t id, hip_api_data_t* data) {\n')
f.write(' switch (id) {\n')
for name, args in api_map.items():
for name in sorted(api_map.keys()):
args = api_map[name]
f.write('// ' + name + str(args) + '\n')
f.write(' case HIP_API_ID_' + name + ':\n')
for ind in range(0, len(args)):
@@ -518,7 +541,8 @@ def generate_prof_header(f, api_map, opts_map):
f.write('static inline const char* hipApiString(hip_api_id_t id, const hip_api_data_t* data) {\n')
f.write(' std::ostringstream oss;\n')
f.write(' switch (id) {\n')
for name, args in api_map.items():
for name in sorted(api_map.keys()):
args = api_map[name]
f.write(' case HIP_API_ID_' + name + ':\n')
f.write(' oss << "' + name + '(";\n')
for ind in range(0, len(args)):
@@ -579,8 +603,8 @@ while len(sys.argv) > 1:
sys.argv.pop(1)
# Usage
if (len(sys.argv) < 3):
fatal ("Usage: " + sys.argv[0] + " [-v] <input HIP API .h file> <patched srcs path> [<output>]\n" +
if (len(sys.argv) < 4):
fatal ("Usage: " + sys.argv[0] + " [-v] <input HIP API .h file> <patched srcs path> <previous output> [<output>]\n" +
" -v - verbose messages\n" +
" -r - process source directory recursively\n" +
" -t - API types matching check\n" +
@@ -589,7 +613,8 @@ if (len(sys.argv) < 3):
" -p - HIP_INIT_API macro patching mode\n" +
"\n" +
" Example:\n" +
" $ " + sys.argv[0] + " -v -p -t --priv ./api/hip/include/hip/amd_detail/hip_runtime_api.h ./api/hip ./api/hip/include/hip/amd_detail/hip_prof_str.h");
" $ " + sys.argv[0] + " -v -p -t --priv ./include/hip/amd_detail/hip_runtime_api.h" +
" ./src ./include/hip/amd_detail/hip_prof_str.h ./include/hip/amd_detail/hip_prof_str.h.new");
# API header file given as an argument
src_pat = "\.cpp$"
@@ -602,7 +627,12 @@ src_dir = sys.argv[2]
if not os.path.isdir(src_dir):
fatal("src directory " + src_dir + "' not found")
if len(sys.argv) > 3: OUTPUT = sys.argv[3]
# Current hip_prof_str include
INPUT = sys.argv[3]
if not os.path.isfile(INPUT):
fatal("input file '" + INPUT + "' not found")
if len(sys.argv) > 4: OUTPUT = sys.argv[4]
# API declaration map
api_map = {
@@ -639,6 +669,27 @@ parse_api(api_hfile, api_map)
# Parsing sources
parse_src(api_map, src_dir, src_pat, opts_map)
try:
cppHeader = CppHeaderParser.CppHeader(INPUT)
except CppHeaderParser.CppParseError as e:
print(e)
sys.exit(1)
# Callback IDs
api_callback_ids = []
for enum in cppHeader.enums:
if enum['name'] == 'hip_api_id_t':
for value in enum['values']:
if value['name'] == 'HIP_API_ID_NONE' or value['name'] == 'HIP_API_ID_FIRST':
continue
if value['name'] == 'HIP_API_ID_LAST':
break
m = re.match(r'HIP_API_ID_(\S*)', value['name'])
if m:
api_callback_ids.append((m.group(1), value['value']))
break
# Checking for non-conformant APIs with missing HIP_INIT macro
for name in list(opts_map.keys()):
m = re.match(r'\.(\S*)', name)
@@ -667,7 +718,10 @@ if not os.path.exists(output_dir):
# Generating output header file
with open(OUTPUT, 'w') as f:
generate_prof_header(f, api_map, opts_map)
generate_prof_header(f, api_map, api_callback_ids, opts_map)
if not filecmp.cmp(INPUT, OUTPUT):
fatal("\"" + INPUT + "\" needs to be re-generated and checked-in with the current changes")
# Successfull exit
sys.exit(0)