From 75f32a24c7c7441c56656ea8c58f07eb05bf2df6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Apr 2019 15:09:33 -0300 Subject: [PATCH] codiff: Improve the comparision of anonymous struct members I.e. 'union {};', 'struct {};' members were always appearing as having been removed, as we normally do lookup by member name, to find out if its offset, size, type, etc changed. For unnamed members, try a different heuristic, i.e. look for the nth anonymous member, this way we're just trying to compare the first unnamed member of, say, struct OLD with the first unnamed member of struct NEW, etc. For OLD == NEW, this works well, for OLD != NEW because some non anonymous field got added, removed or moved around, ditto, and when the number of unnamed fields gets decreased, then we can mix things up, and compare the previously first in A with the previously first in B. For the current intended use case of: 1) compile a .c file into a .o file with debugging info, say FILE.o 2) use 'pfunct --compile FILE.o > regenerated-FILE.c' 3) compile regenerated-FILE.c into regenerated-FILE.o with debugging info 4) codiff --struct FILE.o regenerated-FILE.o and find out if they match This gets us moving forward as we'll spot differences with this algo. For the future we can use a few more heuristics or stop using search by name members, instead traversing both structs in tandem, spotting the differences by comparing the fields that way. Signed-off-by: Arnaldo Carvalho de Melo --- codiff.c | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/codiff.c b/codiff.c index 92376ad..15cef20 100644 --- a/codiff.c +++ b/codiff.c @@ -181,21 +181,42 @@ static int check_print_change(const struct class_member *old, return changes; } +static struct class_member *class__find_pair_member(const struct class *structure, const struct cu *cu, + const struct class_member *pair_member, const struct cu *pair_cu, + int *nr_anonymousp) +{ + const char *member_name = class_member__name(pair_member, pair_cu); + struct class_member *member; + + if (member_name) + return class__find_member_by_name(structure, cu, member_name); + + int nr_anonymous = ++*nr_anonymousp; + + /* Unnamed struct or union, lets look for the first unammed matchin tag.type */ + + type__for_each_member(&structure->type, member) { + if (member->tag.tag == pair_member->tag.tag && /* Both are class/union/struct (unnamed) */ + class_member__name(member, cu) == member_name && /* Both are NULL? */ + --nr_anonymous == 0) + return member; + } + + return NULL; +} + static int check_print_members_changes(const struct class *structure, const struct cu *cu, const struct class *new_structure, const struct cu *new_cu, int print) { - int changes = 0; + int changes = 0, nr_anonymous = 0; struct class_member *member; uint16_t nr_twins_found = 0; type__for_each_member(&structure->type, member) { - const char *member_name = class_member__name(member, cu); - struct class_member *twin = - class__find_member_by_name(new_structure, new_cu, - member_name); + struct class_member *twin = class__find_pair_member(new_structure, new_cu, member, cu, &nr_anonymous); if (twin != NULL) { twin->tag.visited = 1; ++nr_twins_found; @@ -434,21 +455,19 @@ static void show_nr_members_changes(const struct class *structure, const struct cu *new_cu) { struct class_member *member; + int nr_anonymous = 0; /* Find the removed ones */ type__for_each_member(&structure->type, member) { - struct class_member *twin = - class__find_member_by_name(new_structure, new_cu, - class_member__name(member, cu)); + struct class_member *twin = class__find_pair_member(new_structure, new_cu, member, cu, &nr_anonymous); if (twin == NULL) show_changed_member('-', member, cu); } + nr_anonymous = 0; /* Find the new ones */ type__for_each_member(&new_structure->type, member) { - struct class_member *twin = - class__find_member_by_name(structure, cu, - class_member__name(member, new_cu)); + struct class_member *twin = class__find_pair_member(structure, cu, member, new_cu, &nr_anonymous); if (twin == NULL) show_changed_member('+', member, new_cu); }