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 <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2019-04-08 15:09:33 -03:00
parent 6b1e43f2c1
commit 75f32a24c7
1 changed files with 30 additions and 11 deletions

View File

@ -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);
}