From 72d9e8c8aba4445f49bd4a134aeabbb27a985bd9 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Tue, 30 Jun 2020 15:08:06 +0200 Subject: [PATCH] scripts: coccinelle: Scripts for finding wrong device instance usage 2 scripts are provided. - find_functions.cocci (name probably sucks...) - find_dev_usage.cocci (ditto...) find_functions.cocci can patch files where device instance are not const. Then it is used to generate the function database: ./scripts/coccicheck --mode=report --jobs=1 \ --cocci=scripts/coccinelle/find_functions.cocci \ --sp-flag="--include-headers" ./ Then, find_dev_usage.cocci will check if the const qualifier is, or might be lost in a function call. For instance: ./scripts/coccicheck --mode=report --jobs=1 \ --cocci=scripts/coccinelle/find_dev_usage.cocci \ --sp-flag="--include-headers" drivers/i2c Which will output a WARNING on non-zephyr functions calls with a device: ./drivers/i2c/i2c_cc13xx_cc26xx.c:393:5-8: WARNING: in i2c_cc13xx_cc26xx_pm_control calling cb param with dev, check if const qualifier is not lost ./drivers/i2c/i2c_mcux_lpi2c.c:205:40-43: WARNING: in mcux_lpi2c_init calling LPI2C_MasterTransferCreateHandle param with dev, check if const qualifier is not lost ./drivers/i2c/i2c_nrfx_twi.c:258:5-8: WARNING: in twi_nrfx_pm_control calling cb param with dev, check if const qualifier is not lost ./drivers/i2c/i2c_nrfx_twi.c:202:22-25: WARNING: in init_twi calling nrfx_twi_init param with dev, check if const qualifier is not lost ./drivers/i2c/i2c_mcux.c:187:38-41: WARNING: in i2c_mcux_init calling I2C_MasterTransferCreateHandle param with dev, check if const qualifier is not lost ./drivers/i2c/i2c_mcux_flexcomm.c:184:43-46: WARNING: in mcux_flexcomm_init calling I2C_MasterTransferCreateHandle param with dev, check if const qualifier is not lost ./drivers/i2c/i2c_nrfx_twim.c:232:5-8: WARNING: in twim_nrfx_pm_control calling cb param with dev, check if const qualifier is not lost ./drivers/i2c/i2c_nrfx_twim.c:174:8-11: WARNING: in init_twim calling nrfx_twim_init param with dev, check if const qualifier is not lost ./drivers/i2c/i2c_rv32m1_lpi2c.c:246:6-9: WARNING: in rv32m1_lpi2c_init calling LPI2C_MasterTransferCreateHandle param with dev, check if const qualifier is not lost Or: ./scripts/coccicheck --mode=report --jobs=1 \ --cocci=scripts/coccinelle/find_dev_usage.cocci \ --sp-flag="--include-headers" drivers/ieee802154 Which will output an ERROR on using a zephyr function that looses the const qualifier: drivers/ieee802154/ieee802154_rf2xx.c:778:3-6: ERROR: in rf2xx_init calling k_thread_create param with dev, loosing const qualifier, please wrap drivers/ieee802154/ieee802154_nrf5.c:477:19-22: ERROR: in nrf5_init calling k_thread_create param with dev, loosing const qualifier, please wrap drivers/ieee802154/ieee802154_cc1200.c:819:3-6: ERROR: in cc1200_init calling k_thread_create param with dev, loosing const qualifier, please wrap drivers/ieee802154/ieee802154_mcr20a.c:1443:3-6: ERROR: in mcr20a_init calling k_thread_create param with dev, loosing const qualifier, please wrap drivers/ieee802154/ieee802154_cc2520.c:1116:3-6: ERROR: in cc2520_init calling k_thread_create param with dev, loosing const qualifier, please wrap drivers/ieee802154/ieee802154_cc13xx_cc26xx.c:439:32-35: ERROR: in ieee802154_cc13xx_cc26xx_data_init calling k_thread_create param with dev, loosing const qualifier, please wrap ISSUE: - Is it possible to run a set of rules first on all the code, and then another set, both sets being in the same .cocci file? Would be nice to have all at once. Fixes #27399 Signed-off-by: Tomasz Bursztyka --- scripts/coccinelle/find_dev_usage.cocci | 64 ++++++++++++ scripts/coccinelle/find_functions.cocci | 124 ++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 scripts/coccinelle/find_dev_usage.cocci create mode 100644 scripts/coccinelle/find_functions.cocci diff --git a/scripts/coccinelle/find_dev_usage.cocci b/scripts/coccinelle/find_dev_usage.cocci new file mode 100644 index 00000000000..fa969870fba --- /dev/null +++ b/scripts/coccinelle/find_dev_usage.cocci @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Intel Corporation +// SPDX-License-Identifer: Apache-2.0 + +// Uses a python database (a dict) to find where const struct device +// variable are being used in zephyr functions and, if it's being in place +// of a void*, it will print an ERROR for loosing the const qualifier. +// If it's being used on an unknown functions from an external module such +// as a HAL, it will print a WARNING in order to check if the const qualifier +// is not lost. + +virtual report + +//////////////////// +// Initialization // +//////////////////// + +@initialize:python + depends on report +@ +@@ +import pickle + +def check_and_report(F, f, D, nb_args, p): + if f in f_void and int(nb_args) in f_void[f]: + msg = "ERROR: in {} calling {} param with {}, \ +loosing const qualifier, please wrap".format(F, f, D) + coccilib.report.print_report(p[0], msg) + elif f not in f_void and f not in f_other and not f.isupper(): + msg = "WARNING: in {} calling {} param with {}, \ +check if const qualifier is not lost".format(F, f, D) + coccilib.report.print_report(p[0], msg) + +// Loading function data base +with open("function_names.pickle", "rb") as f: + data = pickle.load(f) +f_void = data["f_void"] +f_other = data["f_other"] + + +/////////// +// Rules // +/////////// + +// Find usage of a device instance +@r_find_dev_usage + depends on report +@ +local idexpression struct device *D; +expression list[nb_args] args; +identifier f; +position p; +@@ + f(args, D@p, ...) + + +@script:python + depends on r_find_dev_usage +@ +f << r_find_dev_usage.f; +D << r_find_dev_usage.D; +nb_args << r_find_dev_usage.nb_args; +p << r_find_dev_usage.p; +@@ +check_and_report(p[0].current_element, f, D, nb_args, p) diff --git a/scripts/coccinelle/find_functions.cocci b/scripts/coccinelle/find_functions.cocci new file mode 100644 index 00000000000..ed29d874d11 --- /dev/null +++ b/scripts/coccinelle/find_functions.cocci @@ -0,0 +1,124 @@ +// Copyright (c) 2020 Intel Corporation +// SPDX-License-Identifer: Apache-2.0 + +// In patch mode, patch all device instance to const (if not already). + +// In report mode: +// Generate a q&d python database (a dict actually) of all the +// declared zephyr functions. It will store each function name in 2 +// separate dicts: one storing all function having 1+ void* parameter +// and one for all the other functions. It will store the positions +// of the void* parameter in the first dict, and the actual number of +// parameters in the second. +// Then find_dev_usage.cocci can be used to verify if device instance +// are not loosing their const qualifier. + +virtual patch +virtual report + + +//////////////////// +// Initialization // +//////////////////// + +@initialize:python + depends on report +@ +@@ +import pickle +f_void = {} +f_other = {} + +// Insert a function into right dict depending on parameters +def insert_function(f, params): + void_pos = [] + i = 0 + for prm in params: + if prm.startswith("void *"): + void_pos.append(i) + i += 1 + if len(void_pos) != 0: + f_void[f] = void_pos + else: + f_other[f] = i + 1 + + +/////////// +// Rules // +/////////// + +// Switch device instance to constant. +@r_const_dev + depends on patch + disable optional_qualifier +@ +@@ +-struct device * ++const struct device * + + +// Find function declarations +@r_find_func_declare + depends on report +@ +identifier f; +type ret_type; +parameter list[nb_params] params; +@@ + ret_type f(params); + +// Insert function declaration +@script:python + depends on report +@ +f << r_find_func_declare.f; +params << r_find_func_declare.params; +@@ +insert_function(f, params) + + +// Find function implementations and inlines +// (maybe it should focus on static only? +// but then first rule should not match statics.) +@r_find_func_impl_inlines + depends on report +@ +identifier f; +type ret_type; +parameter list[nb_params] params; +@@ +( + ret_type f(params) + { + ... + } +| + static inline ret_type f(params) + { + ... + } +) + +// Insert function implentations and inlines +@script:python + depends on report +@ +f << r_find_func_impl_inlines.f; +params << r_find_func_impl_inlines.params; +@@ +insert_function(f, params) + + +////////////////// +// Finalization // +////////////////// + +@finalize:python + depends on report +@ +@@ +with open("function_names.pickle", "wb") as f: + data = {} + data['f_void'] = f_void + data['f_other'] = f_other + pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)