From d5399197e9e0d8bad13de5c41df3b93804c0558a Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sun, 8 Jul 2012 23:25:22 +1000 Subject: Allow toggling of semantic checks This patch adds -W and -E options to dtc which allow toggling on and off of the various built in semantic checks on the tree. Signed-off-by: David Gibson --- checks.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++-- dtc.c | 13 +++++++- dtc.h | 1 + tests/dtc-checkfails.sh | 20 ++++++++++-- tests/dtc-fails.sh | 30 ++++++++++++++++++ tests/run_tests.sh | 12 +++++++ 6 files changed, 154 insertions(+), 6 deletions(-) create mode 100755 tests/dtc-fails.sh diff --git a/checks.c b/checks.c index 3080439..9061237 100644 --- a/checks.c +++ b/checks.c @@ -58,7 +58,7 @@ struct check { struct check **prereq; }; -#define CHECK(nm, tfn, nfn, pfn, d, w, e, ...) \ +#define CHECK_ENTRY(nm, tfn, nfn, pfn, d, w, e, ...) \ static struct check *nm##_prereqs[] = { __VA_ARGS__ }; \ static struct check nm = { \ .name = #nm, \ @@ -73,22 +73,30 @@ struct check { .prereq = nm##_prereqs, \ }; #define WARNING(nm, tfn, nfn, pfn, d, ...) \ - CHECK(nm, tfn, nfn, pfn, d, true, false, __VA_ARGS__) + CHECK_ENTRY(nm, tfn, nfn, pfn, d, true, false, __VA_ARGS__) #define ERROR(nm, tfn, nfn, pfn, d, ...) \ - CHECK(nm, tfn, nfn, pfn, d, false, true, __VA_ARGS__) + CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, true, __VA_ARGS__) +#define CHECK(nm, tfn, nfn, pfn, d, ...) \ + CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, false, __VA_ARGS__) #define TREE_WARNING(nm, d, ...) \ WARNING(nm, check_##nm, NULL, NULL, d, __VA_ARGS__) #define TREE_ERROR(nm, d, ...) \ ERROR(nm, check_##nm, NULL, NULL, d, __VA_ARGS__) +#define TREE_CHECK(nm, d, ...) \ + CHECK(nm, check_##nm, NULL, NULL, d, __VA_ARGS__) #define NODE_WARNING(nm, d, ...) \ WARNING(nm, NULL, check_##nm, NULL, d, __VA_ARGS__) #define NODE_ERROR(nm, d, ...) \ ERROR(nm, NULL, check_##nm, NULL, d, __VA_ARGS__) +#define NODE_CHECK(nm, d, ...) \ + CHECK(nm, NULL, check_##nm, NULL, d, __VA_ARGS__) #define PROP_WARNING(nm, d, ...) \ WARNING(nm, NULL, NULL, check_##nm, d, __VA_ARGS__) #define PROP_ERROR(nm, d, ...) \ ERROR(nm, NULL, NULL, check_##nm, d, __VA_ARGS__) +#define PROP_CHECK(nm, d, ...) \ + CHECK(nm, NULL, NULL, check_##nm, d, __VA_ARGS__) #ifdef __GNUC__ static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3))); @@ -179,6 +187,13 @@ out: * Utility check functions */ +/* A check which always fails, for testing purposes only */ +static inline void check_always_fail(struct check *c, struct node *dt) +{ + FAIL(c, "always_fail check"); +} +TREE_CHECK(always_fail, NULL); + static void check_is_string(struct check *c, struct node *root, struct node *node) { @@ -649,8 +664,71 @@ static struct check *check_table[] = { &avoid_default_addr_size, &obsolete_chosen_interrupt_controller, + + &always_fail, }; +static void enable_warning_error(struct check *c, bool warn, bool error) +{ + int i; + + /* Raising level, also raise it for prereqs */ + if ((warn && !c->warn) || (error && !c->error)) + for (i = 0; i < c->num_prereqs; i++) + enable_warning_error(c->prereq[i], warn, error); + + c->warn = c->warn || warn; + c->error = c->error || error; +} + +static void disable_warning_error(struct check *c, bool warn, bool error) +{ + int i; + + /* Lowering level, also lower it for things this is the prereq + * for */ + if ((warn && c->warn) || (error && c->error)) { + for (i = 0; i < ARRAY_SIZE(check_table); i++) { + struct check *cc = check_table[i]; + int j; + + for (j = 0; j < cc->num_prereqs; j++) + if (cc->prereq[j] == c) + disable_warning_error(cc, warn, error); + } + } + + c->warn = c->warn && !warn; + c->error = c->error && !error; +} + +void parse_checks_option(bool warn, bool error, const char *optarg) +{ + int i; + const char *name = optarg; + bool enable = true; + + if ((strncmp(optarg, "no-", 3) == 0) + || (strncmp(optarg, "no_", 3) == 0)) { + name = optarg + 3; + enable = false; + } + + for (i = 0; i < ARRAY_SIZE(check_table); i++) { + struct check *c = check_table[i]; + + if (streq(c->name, name)) { + if (enable) + enable_warning_error(c, warn, error); + else + disable_warning_error(c, warn, error); + return; + } + } + + die("Unrecognized check name \"%s\"\n", name); +} + void process_checks(int force, struct boot_info *bi) { struct node *dt = bi->dt; diff --git a/dtc.c b/dtc.c index bee5085..a375683 100644 --- a/dtc.c +++ b/dtc.c @@ -93,6 +93,9 @@ static void __attribute__ ((noreturn)) usage(void) fprintf(stderr, "\t\t\tlegacy - \"linux,phandle\" properties only\n"); fprintf(stderr, "\t\t\tepapr - \"phandle\" properties only\n"); fprintf(stderr, "\t\t\tboth - Both \"linux,phandle\" and \"phandle\" properties\n"); + fprintf(stderr, "\t-W [no-]\n"); + fprintf(stderr, "\t-E [no-]\n"); + fprintf(stderr, "\t\t\tenable or disable warnings and errors\n"); exit(3); } @@ -115,7 +118,7 @@ int main(int argc, char *argv[]) minsize = 0; padsize = 0; - while ((opt = getopt(argc, argv, "hI:O:o:V:d:R:S:p:fqb:i:vH:s")) + while ((opt = getopt(argc, argv, "hI:O:o:V:d:R:S:p:fqb:i:vH:sW:E:")) != EOF) { switch (opt) { case 'I': @@ -173,6 +176,14 @@ int main(int argc, char *argv[]) sort = 1; break; + case 'W': + parse_checks_option(true, false, optarg); + break; + + case 'E': + parse_checks_option(false, true, optarg); + break; + case 'h': default: usage(); diff --git a/dtc.h b/dtc.h index d57fbfc..7ee2d54 100644 --- a/dtc.h +++ b/dtc.h @@ -226,6 +226,7 @@ void sort_tree(struct boot_info *bi); /* Checks */ +void parse_checks_option(bool warn, bool error, const char *optarg); void process_checks(int force, struct boot_info *bi); /* Flattened trees */ diff --git a/tests/dtc-checkfails.sh b/tests/dtc-checkfails.sh index 3f77b13..76ded15 100755 --- a/tests/dtc-checkfails.sh +++ b/tests/dtc-checkfails.sh @@ -4,10 +4,20 @@ for x; do shift + if [ "$x" = "-n" ]; then + for x; do + shift + if [ "$x" = "--" ]; then + break; + fi + NOCHECKS="$NOCHECKS $x" + done + break; + fi if [ "$x" = "--" ]; then break; fi - CHECKS="$CHECKS $x" + YESCHECKS="$YESCHECKS $x" done LOG=tmp.log.$$ @@ -19,10 +29,16 @@ ret="$?" FAIL_IF_SIGNAL $ret -for c in $CHECKS; do +for c in $YESCHECKS; do if ! grep -E "^(ERROR)|(Warning) \($c\):" $LOG > /dev/null; then FAIL "Failed to trigger check \"$c\"" fi done +for c in $NOCHECKS; do + if grep -E "^(ERROR)|(Warning) \($c\):" $LOG > /dev/null; then + FAIL "Incorrectly triggered check \"$c\"" + fi +done + PASS diff --git a/tests/dtc-fails.sh b/tests/dtc-fails.sh new file mode 100755 index 0000000..4ddcb27 --- /dev/null +++ b/tests/dtc-fails.sh @@ -0,0 +1,30 @@ +#! /bin/sh + +. ./tests.sh + +if [ "$1" = "-n" ]; then + NEG="$1" + shift +fi + +OUTPUT="$1" +shift + +verbose_run $VALGRIND "$DTC" -o "$OUTPUT" "$@" +ret="$?" + +FAIL_IF_SIGNAL $ret + +if [ -n "$NEG" ]; then + if [ ! -e "$OUTPUT" ]; then + FAIL "Produced no output" + fi +else + if [ -e "$OUTPUT" ]; then + FAIL "Incorrectly produced output" + fi +fi + +rm -f "$OUTPUT" + +PASS diff --git a/tests/run_tests.sh b/tests/run_tests.sh index e0299e3..169a829 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -396,6 +396,18 @@ dtc_tests () { run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label5.dts run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label6.dts + # Check warning options + run_sh_test dtc-checkfails.sh address_cells_is_cell interrupt_cells_is_cell -n size_cells_is_cell -- -Wno_size_cells_is_cell -I dts -O dtb bad-ncells.dts + run_sh_test dtc-fails.sh -n test-warn-output.test.dtb -I dts -O dtb bad-ncells.dts + run_sh_test dtc-fails.sh test-error-output.test.dtb -I dts -O dtb bad-ncells.dts -Esize_cells_is_cell + run_sh_test dtc-checkfails.sh always_fail -- -Walways_fail -I dts -O dtb test_tree1.dts + run_sh_test dtc-checkfails.sh -n always_fail -- -Walways_fail -Wno_always_fail -I dts -O dtb test_tree1.dts + run_sh_test dtc-fails.sh test-negation-1.test.dtb -Ealways_fail -I dts -O dtb test_tree1.dts + run_sh_test dtc-fails.sh -n test-negation-2.test.dtb -Ealways_fail -Eno_always_fail -I dts -O dtb test_tree1.dts + run_sh_test dtc-fails.sh test-negation-3.test.dtb -Ealways_fail -Wno_always_fail -I dts -O dtb test_tree1.dts + run_sh_test dtc-fails.sh -n test-negation-4.test.dtb -Esize_cells_is_cell -Eno_size_cells_is_cell -I dts -O dtb bad-ncells.dts + run_sh_test dtc-checkfails.sh size_cells_is_cell -- -Esize_cells_is_cell -Eno_size_cells_is_cell -I dts -O dtb bad-ncells.dts + # Check for proper behaviour reading from stdin run_dtc_test -I dts -O dtb -o stdin_dtc_tree1.test.dtb - < test_tree1.dts run_wrap_test cmp stdin_dtc_tree1.test.dtb dtc_tree1.test.dtb -- cgit v1.2.1