2009-03-14 17:50:36 +01:00
|
|
|
/*
|
2019-01-15 18:28:24 +01:00
|
|
|
SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
Copyright (C) 2006 Mandriva Conectiva S.A.
|
|
|
|
Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
|
|
|
|
*/
|
|
|
|
|
2007-03-30 15:37:37 +02:00
|
|
|
#include <argp.h>
|
2006-11-11 19:31:04 +01:00
|
|
|
#include <assert.h>
|
|
|
|
#include <dwarf.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2008-01-31 13:05:02 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2006-11-11 19:31:04 +01:00
|
|
|
|
2007-01-11 19:07:05 +01:00
|
|
|
#include "dwarves.h"
|
2007-12-16 17:47:59 +01:00
|
|
|
#include "dutil.h"
|
2006-11-11 19:31:04 +01:00
|
|
|
|
2006-11-12 15:41:44 +01:00
|
|
|
static int show_struct_diffs;
|
|
|
|
static int show_function_diffs;
|
2006-11-12 15:56:52 +01:00
|
|
|
static int verbose;
|
2019-04-09 21:46:49 +02:00
|
|
|
static int quiet;
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
static int show_terse_type_changes;
|
2009-07-06 18:44:57 +02:00
|
|
|
|
|
|
|
static struct conf_load conf_load = {
|
2009-07-06 19:00:44 +02:00
|
|
|
.get_addr_info = true,
|
2009-07-06 18:44:57 +02:00
|
|
|
};
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
|
2007-12-17 14:27:24 +01:00
|
|
|
static struct strlist *structs_printed;
|
|
|
|
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
#define TCHANGEF__SIZE (1 << 0)
|
|
|
|
#define TCHANGEF__NR_MEMBERS (1 << 1)
|
|
|
|
#define TCHANGEF__TYPE (1 << 2)
|
|
|
|
#define TCHANGEF__OFFSET (1 << 3)
|
|
|
|
#define TCHANGEF__BIT_OFFSET (1 << 4)
|
|
|
|
#define TCHANGEF__BIT_SIZE (1 << 5)
|
2009-03-03 14:55:25 +01:00
|
|
|
#define TCHANGEF__PADDING (1 << 6)
|
|
|
|
#define TCHANGEF__NR_HOLES (1 << 7)
|
|
|
|
#define TCHANGEF__NR_BIT_HOLES (1 << 8)
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
|
2006-12-28 14:18:43 +01:00
|
|
|
static uint32_t terse_type_changes;
|
2006-11-12 15:41:44 +01:00
|
|
|
|
2006-12-28 14:18:43 +01:00
|
|
|
static uint32_t total_cus_changed;
|
|
|
|
static uint32_t total_nr_functions_changed;
|
|
|
|
static uint32_t total_function_bytes_added;
|
|
|
|
static uint32_t total_function_bytes_removed;
|
2006-11-12 02:14:02 +01:00
|
|
|
|
2007-01-04 04:41:11 +01:00
|
|
|
struct diff_info {
|
|
|
|
const struct tag *tag;
|
|
|
|
const struct cu *cu;
|
|
|
|
int32_t diff;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct diff_info *diff_info__new(const struct tag *twin,
|
|
|
|
const struct cu *cu,
|
|
|
|
int32_t diff)
|
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct diff_info *dinfo = malloc(sizeof(*dinfo));
|
2007-01-04 04:41:11 +01:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (dinfo == NULL) {
|
2007-01-04 04:41:11 +01:00
|
|
|
puts("out of memory!");
|
|
|
|
exit(1);
|
|
|
|
}
|
2012-08-17 23:47:15 +02:00
|
|
|
dinfo->tag = twin;
|
|
|
|
dinfo->cu = cu;
|
|
|
|
dinfo->diff = diff;
|
|
|
|
return dinfo;
|
2007-01-04 04:41:11 +01:00
|
|
|
}
|
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
static void cu__check_max_len_changed_item(struct cu *cu, const char *name,
|
|
|
|
uint8_t addend)
|
|
|
|
{
|
|
|
|
const uint32_t len = strlen(name) + addend;
|
|
|
|
|
|
|
|
if (len > cu->max_len_changed_item)
|
|
|
|
cu->max_len_changed_item = len;
|
|
|
|
}
|
|
|
|
|
2007-01-04 00:57:35 +01:00
|
|
|
static void diff_function(const struct cu *new_cu, struct function *function,
|
|
|
|
struct cu *cu)
|
2006-11-11 19:31:04 +01:00
|
|
|
{
|
2007-01-12 18:47:26 +01:00
|
|
|
struct tag *new_tag;
|
2007-01-07 20:13:39 +01:00
|
|
|
const char *name;
|
2006-11-11 19:31:04 +01:00
|
|
|
|
2007-01-08 15:07:59 +01:00
|
|
|
if (function->inlined || function->abstract_origin != 0)
|
2006-11-11 19:31:04 +01:00
|
|
|
return;
|
|
|
|
|
2007-01-07 20:13:39 +01:00
|
|
|
name = function__name(function, cu);
|
2007-01-12 18:47:26 +01:00
|
|
|
new_tag = cu__find_function_by_name(new_cu, name);
|
|
|
|
if (new_tag != NULL) {
|
|
|
|
struct function *new_function = tag__function(new_tag);
|
2007-01-04 04:41:11 +01:00
|
|
|
int32_t diff = (function__size(new_function) -
|
|
|
|
function__size(function));
|
|
|
|
if (diff != 0) {
|
|
|
|
function->priv = diff_info__new(&new_function->proto.tag, new_cu,
|
|
|
|
diff);
|
2009-04-01 18:47:48 +02:00
|
|
|
cu__check_max_len_changed_item(cu, name, 0);
|
2006-11-11 19:31:04 +01:00
|
|
|
|
2007-01-04 00:57:35 +01:00
|
|
|
++cu->nr_functions_changed;
|
2007-01-04 04:41:11 +01:00
|
|
|
if (diff > 0)
|
|
|
|
cu->function_bytes_added += diff;
|
2006-11-11 19:31:04 +01:00
|
|
|
else
|
2007-01-04 04:41:11 +01:00
|
|
|
cu->function_bytes_removed += -diff;
|
2008-08-06 18:48:28 +02:00
|
|
|
} else {
|
|
|
|
char proto[1024], twin_proto[1024];
|
|
|
|
|
|
|
|
if (strcmp(function__prototype(function, cu,
|
|
|
|
proto, sizeof(proto)),
|
|
|
|
function__prototype(new_function, new_cu,
|
|
|
|
twin_proto,
|
|
|
|
sizeof(twin_proto))) != 0) {
|
|
|
|
++cu->nr_functions_changed;
|
|
|
|
function->priv = diff_info__new(function__tag(new_function),
|
|
|
|
new_cu, 0);
|
|
|
|
}
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
2006-12-24 16:03:33 +01:00
|
|
|
} else {
|
2007-01-04 04:41:11 +01:00
|
|
|
const uint32_t diff = -function__size(function);
|
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
cu__check_max_len_changed_item(cu, name, 0);
|
2007-01-04 04:41:11 +01:00
|
|
|
function->priv = diff_info__new(NULL, NULL, diff);
|
2007-01-04 00:57:35 +01:00
|
|
|
++cu->nr_functions_changed;
|
2007-01-04 04:41:11 +01:00
|
|
|
cu->function_bytes_removed += -diff;
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
static int check_print_change(const struct class_member *old,
|
2007-01-04 04:41:11 +01:00
|
|
|
const struct cu *old_cu,
|
|
|
|
const struct class_member *new,
|
|
|
|
const struct cu *new_cu,
|
|
|
|
int print)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
{
|
2007-01-09 18:10:57 +01:00
|
|
|
size_t old_size, new_size;
|
|
|
|
char old_type_name[128], new_type_name[128];
|
2009-03-18 16:17:07 +01:00
|
|
|
const struct tag *old_type = cu__type(old_cu, old->tag.type);
|
|
|
|
const struct tag *new_type = cu__type(new_cu, new->tag.type);
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
int changes = 0;
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
|
2007-01-09 18:10:57 +01:00
|
|
|
if (old_type == NULL || new_type == NULL)
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
return 0;
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
|
2009-03-17 17:57:40 +01:00
|
|
|
old_size = old->byte_size;
|
|
|
|
new_size = new->byte_size;
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
if (old_size != new_size)
|
|
|
|
changes = 1;
|
|
|
|
|
2009-03-17 15:43:17 +01:00
|
|
|
if (old->byte_offset != new->byte_offset) {
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
changes = 1;
|
|
|
|
terse_type_changes |= TCHANGEF__OFFSET;
|
|
|
|
}
|
|
|
|
|
2009-03-17 15:43:17 +01:00
|
|
|
if (old->bitfield_offset != new->bitfield_offset) {
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
changes = 1;
|
|
|
|
terse_type_changes |= TCHANGEF__BIT_OFFSET;
|
|
|
|
}
|
|
|
|
|
2009-03-17 15:43:17 +01:00
|
|
|
if (old->bitfield_size != new->bitfield_size) {
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
changes = 1;
|
|
|
|
terse_type_changes |= TCHANGEF__BIT_SIZE;
|
|
|
|
}
|
|
|
|
|
2007-01-09 18:10:57 +01:00
|
|
|
if (strcmp(tag__name(old_type, old_cu, old_type_name,
|
2009-08-24 22:22:43 +02:00
|
|
|
sizeof(old_type_name), NULL),
|
2007-01-09 18:10:57 +01:00
|
|
|
tag__name(new_type, new_cu, new_type_name,
|
2009-08-24 22:22:43 +02:00
|
|
|
sizeof(new_type_name), NULL)) != 0) {
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
changes = 1;
|
|
|
|
terse_type_changes |= TCHANGEF__TYPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changes && print && !show_terse_type_changes)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
printf(" %s\n"
|
2009-03-03 14:31:14 +01:00
|
|
|
" from: %-21s /* %5u(%2u) %5zd(%2d) */\n"
|
|
|
|
" to: %-21s /* %5u(%2u) %5zd(%2u) */\n",
|
2009-04-02 22:54:43 +02:00
|
|
|
class_member__name(old, old_cu),
|
2009-03-17 15:43:17 +01:00
|
|
|
old_type_name, old->byte_offset, old->bitfield_offset,
|
|
|
|
old_size, old->bitfield_size,
|
|
|
|
new_type_name, new->byte_offset, new->bitfield_offset,
|
|
|
|
new_size, new->bitfield_size);
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
|
|
|
|
return changes;
|
|
|
|
}
|
|
|
|
|
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>
2019-04-08 20:09:33 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
static int check_print_members_changes(const struct class *structure,
|
2007-01-04 04:41:11 +01:00
|
|
|
const struct cu *cu,
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
const struct class *new_structure,
|
2007-01-04 04:41:11 +01:00
|
|
|
const struct cu *new_cu,
|
2006-11-12 19:59:47 +01:00
|
|
|
int print)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
{
|
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>
2019-04-08 20:09:33 +02:00
|
|
|
int changes = 0, nr_anonymous = 0;
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
struct class_member *member;
|
2009-03-03 14:31:14 +01:00
|
|
|
uint16_t nr_twins_found = 0;
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
|
2007-05-24 02:43:01 +02:00
|
|
|
type__for_each_member(&structure->type, member) {
|
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>
2019-04-08 20:09:33 +02:00
|
|
|
struct class_member *twin = class__find_pair_member(new_structure, new_cu, member, cu, &nr_anonymous);
|
2009-03-03 14:31:14 +01:00
|
|
|
if (twin != NULL) {
|
2009-03-03 14:56:37 +01:00
|
|
|
twin->tag.visited = 1;
|
2009-03-03 14:31:14 +01:00
|
|
|
++nr_twins_found;
|
2007-01-04 04:41:11 +01:00
|
|
|
if (check_print_change(member, cu, twin, new_cu, print))
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
changes = 1;
|
2009-03-03 14:31:14 +01:00
|
|
|
} else {
|
|
|
|
changes = 1;
|
|
|
|
if (print) {
|
|
|
|
char name[128];
|
|
|
|
struct tag *type;
|
2009-03-18 16:17:07 +01:00
|
|
|
type = cu__type(cu, member->tag.type);
|
2009-03-03 14:31:14 +01:00
|
|
|
printf(" %s\n"
|
|
|
|
" removed: %-21s /* %5u(%2u) %5zd(%2d) */\n",
|
2009-04-02 22:54:43 +02:00
|
|
|
class_member__name(member, cu),
|
2009-08-24 22:22:43 +02:00
|
|
|
tag__name(type, cu, name, sizeof(name), NULL),
|
2009-03-17 15:43:17 +01:00
|
|
|
member->byte_offset, member->bitfield_offset,
|
2009-03-17 17:57:40 +01:00
|
|
|
member->byte_size, member->bitfield_size);
|
2009-03-03 14:31:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-20 19:42:17 +02:00
|
|
|
if (nr_twins_found == (new_structure->type.nr_members +
|
|
|
|
new_structure->type.nr_static_members))
|
2009-03-03 14:31:14 +01:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
changes = 1;
|
|
|
|
if (!print)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
type__for_each_member(&new_structure->type, member) {
|
2009-03-03 14:56:37 +01:00
|
|
|
if (!member->tag.visited) {
|
2009-03-03 14:31:14 +01:00
|
|
|
char name[128];
|
|
|
|
struct tag *type;
|
2009-03-18 16:17:07 +01:00
|
|
|
type = cu__type(new_cu, member->tag.type);
|
2009-03-03 14:31:14 +01:00
|
|
|
printf(" %s\n"
|
|
|
|
" added: %-21s /* %5u(%2u) %5zd(%2d) */\n",
|
2009-04-02 22:54:43 +02:00
|
|
|
class_member__name(member, new_cu),
|
2009-08-24 22:22:43 +02:00
|
|
|
tag__name(type, new_cu, name, sizeof(name), NULL),
|
2009-03-17 15:43:17 +01:00
|
|
|
member->byte_offset, member->bitfield_offset,
|
2009-03-17 17:57:40 +01:00
|
|
|
member->byte_size, member->bitfield_size);
|
2009-03-03 14:31:14 +01:00
|
|
|
}
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
}
|
2009-03-03 14:31:14 +01:00
|
|
|
out:
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
return changes;
|
|
|
|
}
|
|
|
|
|
2007-01-04 04:41:11 +01:00
|
|
|
static void diff_struct(const struct cu *new_cu, struct class *structure,
|
|
|
|
struct cu *cu)
|
2006-11-12 15:29:33 +01:00
|
|
|
{
|
2007-01-07 16:11:20 +01:00
|
|
|
struct tag *new_tag;
|
2007-03-30 15:59:04 +02:00
|
|
|
struct class *new_structure = NULL;
|
2007-01-04 04:41:11 +01:00
|
|
|
int32_t diff;
|
2006-11-12 15:29:33 +01:00
|
|
|
|
2007-05-31 06:53:01 +02:00
|
|
|
assert(class__is_struct(structure));
|
2006-11-12 15:29:33 +01:00
|
|
|
|
2009-04-02 22:54:43 +02:00
|
|
|
if (class__size(structure) == 0 || class__name(structure, cu) == NULL)
|
2006-11-12 15:29:33 +01:00
|
|
|
return;
|
|
|
|
|
2009-03-06 14:57:41 +01:00
|
|
|
new_tag = cu__find_struct_by_name(new_cu,
|
2009-04-02 22:54:43 +02:00
|
|
|
class__name(structure, cu), 0, NULL);
|
2007-09-26 19:23:11 +02:00
|
|
|
if (new_tag == NULL)
|
|
|
|
return;
|
2006-12-24 16:35:21 +01:00
|
|
|
|
2007-01-07 16:11:20 +01:00
|
|
|
new_structure = tag__class(new_tag);
|
2007-01-09 13:00:47 +01:00
|
|
|
if (class__size(new_structure) == 0)
|
2006-11-12 15:29:33 +01:00
|
|
|
return;
|
|
|
|
|
2007-05-31 06:53:01 +02:00
|
|
|
assert(class__is_struct(new_structure));
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
|
2007-01-09 13:00:47 +01:00
|
|
|
diff = class__size(structure) != class__size(new_structure) ||
|
|
|
|
class__nr_members(structure) != class__nr_members(new_structure) ||
|
2007-01-04 04:41:11 +01:00
|
|
|
check_print_members_changes(structure, cu,
|
2009-03-03 14:55:25 +01:00
|
|
|
new_structure, new_cu, 0) ||
|
|
|
|
structure->padding != new_structure->padding ||
|
|
|
|
structure->nr_holes != new_structure->nr_holes ||
|
|
|
|
structure->nr_bit_holes != new_structure->nr_bit_holes;
|
|
|
|
|
2007-01-04 04:41:11 +01:00
|
|
|
if (diff == 0)
|
2006-11-12 15:29:33 +01:00
|
|
|
return;
|
2007-12-08 04:50:25 +01:00
|
|
|
|
2007-01-04 04:41:11 +01:00
|
|
|
++cu->nr_structures_changed;
|
2009-04-02 22:54:43 +02:00
|
|
|
cu__check_max_len_changed_item(cu, class__name(structure, cu),
|
|
|
|
sizeof("struct"));
|
2007-01-07 15:30:58 +01:00
|
|
|
structure->priv = diff_info__new(class__tag(new_structure),
|
|
|
|
new_cu, diff);
|
2006-11-12 15:29:33 +01:00
|
|
|
}
|
|
|
|
|
2019-04-08 20:07:17 +02:00
|
|
|
static struct cu *cus__find_pair(struct cus *cus, const char *name)
|
|
|
|
{
|
|
|
|
if (cus->nr_entries == 1)
|
|
|
|
return list_first_entry(&cus->cus, struct cu, node);
|
|
|
|
|
|
|
|
return cus__find_cu_by_name(cus, name);
|
|
|
|
}
|
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
static int cu_find_new_tags_iterator(struct cu *new_cu, void *old_cus)
|
2007-01-04 00:29:24 +01:00
|
|
|
{
|
2019-04-08 20:07:17 +02:00
|
|
|
struct cu *old_cu = cus__find_pair(old_cus, new_cu->name);
|
2006-12-24 16:03:33 +01:00
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
if (old_cu != NULL && cu__same_build_id(old_cu, new_cu))
|
2006-12-24 16:03:33 +01:00
|
|
|
return 0;
|
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
struct function *function;
|
|
|
|
uint32_t id;
|
|
|
|
cu__for_each_function(new_cu, id, function) {
|
|
|
|
/*
|
|
|
|
* We're not interested in aliases, just real function definitions,
|
|
|
|
* where we'll know if the kind of inlining
|
|
|
|
*/
|
|
|
|
if (function->abstract_origin || function->inlined)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const char *name = function__name(function, new_cu);
|
|
|
|
struct tag *old_function = cu__find_function_by_name(old_cu,
|
|
|
|
name);
|
|
|
|
if (old_function != NULL && !tag__function(old_function)->inlined)
|
|
|
|
continue;
|
2008-01-03 16:03:32 +01:00
|
|
|
|
2007-01-04 04:41:11 +01:00
|
|
|
const int32_t diff = function__size(function);
|
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
cu__check_max_len_changed_item(new_cu, name, 0);
|
|
|
|
++new_cu->nr_functions_changed;
|
|
|
|
new_cu->function_bytes_added += diff;
|
|
|
|
function->priv = diff_info__new(old_function, new_cu, diff);
|
2006-12-24 16:03:33 +01:00
|
|
|
}
|
|
|
|
|
2006-12-29 18:28:58 +01:00
|
|
|
struct class *class;
|
2009-04-01 18:47:48 +02:00
|
|
|
cu__for_each_struct(new_cu, id, class) {
|
2009-04-02 22:54:43 +02:00
|
|
|
const char *name = class__name(class, new_cu);
|
2009-04-01 18:47:48 +02:00
|
|
|
if (name == NULL || class__size(class) == 0 ||
|
|
|
|
cu__find_struct_by_name(old_cu, name, 0, NULL))
|
|
|
|
continue;
|
2006-12-24 16:35:21 +01:00
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
class->priv = diff_info__new(NULL, NULL, 1);
|
|
|
|
++new_cu->nr_structures_changed;
|
2006-12-24 16:35:21 +01:00
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
cu__check_max_len_changed_item(new_cu, name, sizeof("struct"));
|
2008-01-04 02:34:51 +01:00
|
|
|
}
|
2006-12-24 16:03:33 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
static int cu_diff_iterator(struct cu *cu, void *new_cus)
|
|
|
|
{
|
2019-04-08 20:07:17 +02:00
|
|
|
struct cu *new_cu = cus__find_pair(new_cus, cu->name);
|
2006-11-11 19:31:04 +01:00
|
|
|
|
2008-01-31 13:05:02 +01:00
|
|
|
if (new_cu != NULL && cu__same_build_id(cu, new_cu))
|
|
|
|
return 0;
|
2009-04-01 18:47:48 +02:00
|
|
|
|
|
|
|
uint32_t id;
|
|
|
|
struct class *class;
|
|
|
|
cu__for_each_struct(cu, id, class)
|
|
|
|
diff_struct(new_cu, class, cu);
|
|
|
|
|
|
|
|
struct function *function;
|
|
|
|
cu__for_each_function(cu, id, function)
|
|
|
|
diff_function(new_cu, function, cu);
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-01-09 15:16:46 +01:00
|
|
|
static void show_diffs_function(struct function *function, const struct cu *cu,
|
|
|
|
const void *cookie)
|
2006-11-11 19:31:04 +01:00
|
|
|
{
|
2007-01-04 04:41:11 +01:00
|
|
|
const struct diff_info *di = function->priv;
|
|
|
|
|
2007-01-08 15:21:47 +01:00
|
|
|
printf(" %-*.*s | %+4d",
|
2007-03-28 16:38:32 +02:00
|
|
|
(int)cu->max_len_changed_item, (int)cu->max_len_changed_item,
|
2007-01-07 20:13:39 +01:00
|
|
|
function__name(function, cu), di->diff);
|
2007-01-08 15:21:47 +01:00
|
|
|
|
|
|
|
if (!verbose) {
|
|
|
|
putchar('\n');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (di->tag == NULL)
|
2007-01-09 15:16:46 +01:00
|
|
|
puts(cookie ? " (added)" : " (removed)");
|
2007-01-08 15:21:47 +01:00
|
|
|
else {
|
2008-10-02 19:34:42 +02:00
|
|
|
struct function *twin = tag__function(di->tag);
|
2007-01-08 15:21:47 +01:00
|
|
|
|
2008-01-03 16:03:32 +01:00
|
|
|
if (twin->inlined)
|
|
|
|
puts(cookie ? " (uninlined)" : " (inlined)");
|
2008-10-02 19:34:42 +02:00
|
|
|
else if (strcmp(function__name(function, cu),
|
|
|
|
function__name(twin, di->cu)) != 0)
|
2007-01-08 15:21:47 +01:00
|
|
|
printf("%s: BRAIN FART ALERT: comparing %s to %s, "
|
|
|
|
"should be the same name\n", __FUNCTION__,
|
2008-10-02 19:34:42 +02:00
|
|
|
function__name(function, cu),
|
|
|
|
function__name(twin, di->cu));
|
2007-02-25 02:12:34 +01:00
|
|
|
else {
|
2008-08-06 18:48:28 +02:00
|
|
|
char proto[1024], twin_proto[1024];
|
|
|
|
|
2009-03-07 21:37:33 +01:00
|
|
|
printf(" # %d -> %d", function__size(function),
|
2007-01-08 15:21:47 +01:00
|
|
|
function__size(twin));
|
2007-02-25 02:12:34 +01:00
|
|
|
if (function->lexblock.nr_lexblocks !=
|
|
|
|
twin->lexblock.nr_lexblocks)
|
|
|
|
printf(", lexblocks: %d -> %d",
|
|
|
|
function->lexblock.nr_lexblocks,
|
|
|
|
twin->lexblock.nr_lexblocks);
|
|
|
|
if (function->lexblock.nr_inline_expansions !=
|
|
|
|
twin->lexblock.nr_inline_expansions)
|
|
|
|
printf(", # inlines: %d -> %d",
|
|
|
|
function->lexblock.nr_inline_expansions,
|
|
|
|
twin->lexblock.nr_inline_expansions);
|
|
|
|
if (function->lexblock.size_inline_expansions !=
|
|
|
|
twin->lexblock.size_inline_expansions)
|
2009-03-07 20:45:09 +01:00
|
|
|
printf(", size inlines: %d -> %d",
|
2007-02-25 02:12:34 +01:00
|
|
|
function->lexblock.size_inline_expansions,
|
|
|
|
twin->lexblock.size_inline_expansions);
|
2008-08-06 18:48:28 +02:00
|
|
|
|
|
|
|
if (strcmp(function__prototype(function, cu,
|
|
|
|
proto, sizeof(proto)),
|
|
|
|
function__prototype(twin, di->cu,
|
|
|
|
twin_proto, sizeof(twin_proto))) != 0)
|
|
|
|
printf(", prototype: %s -> %s", proto, twin_proto);
|
2007-02-25 02:12:34 +01:00
|
|
|
putchar('\n');
|
|
|
|
}
|
2007-01-08 15:21:47 +01:00
|
|
|
}
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
|
|
|
|
2007-01-04 04:41:11 +01:00
|
|
|
static void show_changed_member(char change, const struct class_member *member,
|
|
|
|
const struct cu *cu)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
{
|
2009-03-18 16:17:07 +01:00
|
|
|
const struct tag *type = cu__type(cu, member->tag.type);
|
2007-01-09 18:10:57 +01:00
|
|
|
char bf[128];
|
|
|
|
|
2008-04-20 23:56:36 +02:00
|
|
|
tag__assert_search_result(type);
|
2007-03-28 16:38:32 +02:00
|
|
|
printf(" %c%-26s %-21s /* %5u %5zd */\n",
|
2009-08-24 22:22:43 +02:00
|
|
|
change, tag__name(type, cu, bf, sizeof(bf), NULL),
|
2009-04-02 22:54:43 +02:00
|
|
|
class_member__name(member, cu),
|
2009-03-17 17:57:40 +01:00
|
|
|
member->byte_offset, member->byte_size);
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void show_nr_members_changes(const struct class *structure,
|
2007-01-04 04:41:11 +01:00
|
|
|
const struct cu *cu,
|
|
|
|
const struct class *new_structure,
|
|
|
|
const struct cu *new_cu)
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
{
|
|
|
|
struct class_member *member;
|
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>
2019-04-08 20:09:33 +02:00
|
|
|
int nr_anonymous = 0;
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
|
|
|
|
/* Find the removed ones */
|
2007-05-24 02:43:01 +02:00
|
|
|
type__for_each_member(&structure->type, member) {
|
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>
2019-04-08 20:09:33 +02:00
|
|
|
struct class_member *twin = class__find_pair_member(new_structure, new_cu, member, cu, &nr_anonymous);
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
if (twin == NULL)
|
2007-01-04 04:41:11 +01:00
|
|
|
show_changed_member('-', member, cu);
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
}
|
|
|
|
|
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>
2019-04-08 20:09:33 +02:00
|
|
|
nr_anonymous = 0;
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
/* Find the new ones */
|
2007-05-24 02:43:01 +02:00
|
|
|
type__for_each_member(&new_structure->type, member) {
|
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>
2019-04-08 20:09:33 +02:00
|
|
|
struct class_member *twin = class__find_pair_member(structure, cu, member, new_cu, &nr_anonymous);
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
if (twin == NULL)
|
2007-01-04 04:41:11 +01:00
|
|
|
show_changed_member('+', member, new_cu);
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-02 22:54:43 +02:00
|
|
|
static void print_terse_type_changes(struct class *structure,
|
|
|
|
const struct cu *cu)
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
{
|
|
|
|
const char *sep = "";
|
|
|
|
|
2009-04-02 22:54:43 +02:00
|
|
|
printf("struct %s: ", class__name(structure, cu));
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
|
|
|
|
if (terse_type_changes & TCHANGEF__SIZE) {
|
|
|
|
fputs("size", stdout);
|
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
if (terse_type_changes & TCHANGEF__NR_MEMBERS) {
|
|
|
|
printf("%snr_members", sep);
|
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
if (terse_type_changes & TCHANGEF__TYPE) {
|
|
|
|
printf("%stype", sep);
|
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
if (terse_type_changes & TCHANGEF__OFFSET) {
|
|
|
|
printf("%soffset", sep);
|
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
if (terse_type_changes & TCHANGEF__BIT_OFFSET) {
|
|
|
|
printf("%sbit_offset", sep);
|
|
|
|
sep = ", ";
|
|
|
|
}
|
2009-03-03 14:55:25 +01:00
|
|
|
if (terse_type_changes & TCHANGEF__BIT_SIZE) {
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
printf("%sbit_size", sep);
|
2009-03-03 14:55:25 +01:00
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
if (terse_type_changes & TCHANGEF__PADDING) {
|
|
|
|
printf("%spadding", sep);
|
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
if (terse_type_changes & TCHANGEF__NR_HOLES) {
|
|
|
|
printf("%snr_holes", sep);
|
|
|
|
sep = ", ";
|
|
|
|
}
|
|
|
|
if (terse_type_changes & TCHANGEF__NR_BIT_HOLES)
|
|
|
|
printf("%snr_bit_holes", sep);
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
|
|
|
|
putchar('\n');
|
|
|
|
}
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
|
2007-05-24 23:45:34 +02:00
|
|
|
static void show_diffs_structure(struct class *structure,
|
2007-01-04 04:41:11 +01:00
|
|
|
const struct cu *cu)
|
2006-11-11 19:31:04 +01:00
|
|
|
{
|
2007-01-04 04:41:11 +01:00
|
|
|
const struct diff_info *di = structure->priv;
|
2007-09-26 19:23:11 +02:00
|
|
|
const struct class *new_structure;
|
|
|
|
int diff;
|
|
|
|
/*
|
|
|
|
* This is when the struct was not present in the new object file.
|
|
|
|
* Meaning that it either was not referenced or that it was completely
|
|
|
|
* removed.
|
|
|
|
*/
|
|
|
|
if (di == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
new_structure = tag__class(di->tag);
|
2007-09-27 15:18:42 +02:00
|
|
|
/*
|
|
|
|
* If there is a diff_info but its di->tag is NULL we have a new structure,
|
|
|
|
* one that didn't appears in the old object. See find_new_classes_iterator.
|
|
|
|
*/
|
|
|
|
if (new_structure == NULL)
|
|
|
|
diff = class__size(structure);
|
|
|
|
else
|
|
|
|
diff = class__size(new_structure) - class__size(structure);
|
2006-11-12 15:56:52 +01:00
|
|
|
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
terse_type_changes = 0;
|
|
|
|
|
|
|
|
if (!show_terse_type_changes)
|
|
|
|
printf(" struct %-*.*s | %+4d\n",
|
2007-03-28 16:38:32 +02:00
|
|
|
(int)(cu->max_len_changed_item - sizeof("struct")),
|
|
|
|
(int)(cu->max_len_changed_item - sizeof("struct")),
|
2009-04-02 22:54:43 +02:00
|
|
|
class__name(structure, cu), diff);
|
2006-11-12 15:56:52 +01:00
|
|
|
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
if (diff != 0)
|
|
|
|
terse_type_changes |= TCHANGEF__SIZE;
|
|
|
|
|
|
|
|
if (!verbose && !show_terse_type_changes)
|
2006-11-12 15:56:52 +01:00
|
|
|
return;
|
|
|
|
|
2006-12-24 16:35:21 +01:00
|
|
|
if (new_structure == NULL)
|
2007-01-09 13:00:47 +01:00
|
|
|
diff = -class__nr_members(structure);
|
2006-12-24 16:35:21 +01:00
|
|
|
else
|
2007-01-09 13:00:47 +01:00
|
|
|
diff = (class__nr_members(new_structure) -
|
|
|
|
class__nr_members(structure));
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
if (diff != 0) {
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
terse_type_changes |= TCHANGEF__NR_MEMBERS;
|
|
|
|
if (!show_terse_type_changes) {
|
|
|
|
printf(" nr_members: %+d\n", diff);
|
2006-12-24 16:35:21 +01:00
|
|
|
if (new_structure != NULL)
|
2007-01-04 04:41:11 +01:00
|
|
|
show_nr_members_changes(structure, cu,
|
|
|
|
new_structure, di->cu);
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
}
|
[CODIFF]: Detect and print all sorts of changes in structs
[acme@newtoy examples]$ cat struct.c
static struct foo {
char a:2;
unsigned int b;
unsigned long c;
unsigned long d;
unsigned long e;
} bar;
int main(int argc, char *argv[])
{
printf("%d", bar.a);
}
[acme@newtoy examples]$
Then change "a:2" to "a:4":
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
1 struct changed
Now, on top of that move a after b:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 4(4) 1(4) */
b;
from: unsigned int /* 4(0) 4(0) */
to: unsigned int /* 0(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Move it back a to before b and change the type of e without changing its size,
i.e. from unsigned long to long:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | +0
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 16(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
Now on top of this lets delete the c member:
[acme@newtoy examples]$ codiff -V old_struct new_struct
struct.c:
struct foo | -4
nr_members: -1
-long unsigned int c; /* 8 4 */
a:2;
from: char /* 0(6) 1(2) */
to: char /* 0(4) 1(4) */
d;
from: long unsigned int /* 12(0) 4(0) */
to: long unsigned int /* 8(0) 4(0) */
e;
from: long unsigned int /* 16(0) 4(0) */
to: long int /* 12(0) 4(0) */
1 struct changed
[acme@newtoy examples]$
WOW, many changes, what an ABI breakage, no? :-)
It started as:
[acme@newtoy examples]$ pahole old_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:2; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int c; /* 8 4 */
long unsigned int d; /* 12 4 */
long unsigned int e; /* 16 4 */
}; /* size: 20, sum members: 17, holes: 1, sum holes: 3 */
And ended up as:
[acme@newtoy examples]$ pahole new_struct foo
/* /home/acme/pahole/examples/struct.c:3 */
struct foo {
char a:4; /* 0 1 */
/* XXX 3 bytes hole, try to pack */
unsigned int b; /* 4 4 */
long unsigned int d; /* 8 4 */
long int e; /* 12 4 */
}; /* size: 16, sum members: 13, holes: 1, sum holes: 3 */
[acme@newtoy examples]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 18:07:21 +01:00
|
|
|
}
|
2009-03-03 14:55:25 +01:00
|
|
|
if (new_structure != NULL) {
|
|
|
|
diff = (int)new_structure->padding - (int)structure->padding;
|
|
|
|
if (diff) {
|
|
|
|
terse_type_changes |= TCHANGEF__PADDING;
|
|
|
|
if (!show_terse_type_changes)
|
|
|
|
printf(" padding: %+d\n", diff);
|
|
|
|
}
|
|
|
|
diff = (int)new_structure->nr_holes - (int)structure->nr_holes;
|
|
|
|
if (diff) {
|
|
|
|
terse_type_changes |= TCHANGEF__NR_HOLES;
|
|
|
|
if (!show_terse_type_changes)
|
|
|
|
printf(" nr_holes: %+d\n", diff);
|
|
|
|
}
|
|
|
|
diff = ((int)new_structure->nr_bit_holes -
|
|
|
|
(int)structure->nr_bit_holes);
|
|
|
|
if (structure->nr_bit_holes != new_structure->nr_bit_holes) {
|
|
|
|
terse_type_changes |= TCHANGEF__NR_BIT_HOLES;
|
|
|
|
if (!show_terse_type_changes)
|
|
|
|
printf(" nr_bit_holes: %+d\n", diff);
|
|
|
|
}
|
2007-01-04 04:41:11 +01:00
|
|
|
check_print_members_changes(structure, cu,
|
|
|
|
new_structure, di->cu, 1);
|
2009-03-03 14:55:25 +01:00
|
|
|
}
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
if (show_terse_type_changes)
|
2009-04-02 22:54:43 +02:00
|
|
|
print_terse_type_changes(structure, cu);
|
2006-11-12 15:29:33 +01:00
|
|
|
}
|
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
static void show_structure_diffs_iterator(struct class *class, struct cu *cu)
|
2006-11-12 15:29:33 +01:00
|
|
|
{
|
2007-12-17 14:27:24 +01:00
|
|
|
if (class->priv != NULL) {
|
2009-04-02 22:54:43 +02:00
|
|
|
const char *name = class__name(class, cu);
|
2007-12-17 14:27:24 +01:00
|
|
|
if (!strlist__has_entry(structs_printed, name)) {
|
|
|
|
show_diffs_structure(class, cu);
|
|
|
|
strlist__add(structs_printed, name);
|
|
|
|
}
|
|
|
|
}
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int cu_show_diffs_iterator(struct cu *cu, void *cookie)
|
|
|
|
{
|
|
|
|
static int first_cu_printed;
|
|
|
|
|
2006-11-12 15:29:33 +01:00
|
|
|
if (cu->nr_functions_changed == 0 &&
|
|
|
|
cu->nr_structures_changed == 0)
|
2006-11-11 19:31:04 +01:00
|
|
|
return 0;
|
|
|
|
|
2019-04-09 21:46:49 +02:00
|
|
|
if (first_cu_printed) {
|
|
|
|
if (!quiet)
|
|
|
|
putchar('\n');
|
|
|
|
} else {
|
2006-11-11 19:31:04 +01:00
|
|
|
first_cu_printed = 1;
|
2019-04-09 21:46:49 +02:00
|
|
|
}
|
2006-11-12 02:14:02 +01:00
|
|
|
|
|
|
|
++total_cus_changed;
|
|
|
|
|
2019-04-09 21:46:49 +02:00
|
|
|
if (!quiet)
|
|
|
|
printf("%s:\n", cu->name);
|
2006-11-12 15:29:33 +01:00
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
uint32_t id;
|
|
|
|
struct class *class;
|
|
|
|
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
if (show_terse_type_changes) {
|
2009-04-01 18:47:48 +02:00
|
|
|
cu__for_each_struct(cu, id, class)
|
|
|
|
show_structure_diffs_iterator(class, cu);
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-12 15:41:44 +01:00
|
|
|
if (cu->nr_structures_changed != 0 && show_struct_diffs) {
|
2009-04-01 18:47:48 +02:00
|
|
|
cu__for_each_struct(cu, id, class)
|
|
|
|
show_structure_diffs_iterator(class, cu);
|
2006-11-12 15:29:33 +01:00
|
|
|
printf(" %u struct%s changed\n", cu->nr_structures_changed,
|
|
|
|
cu->nr_structures_changed > 1 ? "s" : "");
|
2006-11-12 02:14:02 +01:00
|
|
|
}
|
2006-11-12 15:29:33 +01:00
|
|
|
|
2006-11-12 15:41:44 +01:00
|
|
|
if (cu->nr_functions_changed != 0 && show_function_diffs) {
|
2006-11-12 15:29:33 +01:00
|
|
|
total_nr_functions_changed += cu->nr_functions_changed;
|
|
|
|
|
2009-04-01 18:47:48 +02:00
|
|
|
struct function *function;
|
|
|
|
cu__for_each_function(cu, id, function) {
|
|
|
|
if (function->priv != NULL)
|
|
|
|
show_diffs_function(function, cu, cookie);
|
|
|
|
}
|
|
|
|
|
2006-11-12 15:29:33 +01:00
|
|
|
printf(" %u function%s changed", cu->nr_functions_changed,
|
|
|
|
cu->nr_functions_changed > 1 ? "s" : "");
|
|
|
|
if (cu->function_bytes_added != 0) {
|
|
|
|
total_function_bytes_added += cu->function_bytes_added;
|
2007-03-28 16:38:32 +02:00
|
|
|
printf(", %zd bytes added", cu->function_bytes_added);
|
2006-11-12 15:29:33 +01:00
|
|
|
}
|
|
|
|
if (cu->function_bytes_removed != 0) {
|
|
|
|
total_function_bytes_removed += cu->function_bytes_removed;
|
2007-03-28 16:38:32 +02:00
|
|
|
printf(", %zd bytes removed",
|
|
|
|
cu->function_bytes_removed);
|
2006-11-12 15:29:33 +01:00
|
|
|
}
|
2008-01-03 17:55:20 +01:00
|
|
|
printf(", diff: %+zd",
|
|
|
|
cu->function_bytes_added - cu->function_bytes_removed);
|
2006-11-12 15:29:33 +01:00
|
|
|
putchar('\n');
|
2006-11-12 02:14:02 +01:00
|
|
|
}
|
2006-11-11 19:31:04 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-11 16:31:17 +01:00
|
|
|
static int cu_delete_priv(struct cu *cu, void *cookie __unused)
|
|
|
|
{
|
|
|
|
struct class *c;
|
|
|
|
struct function *f;
|
|
|
|
uint32_t id;
|
|
|
|
|
|
|
|
cu__for_each_struct(cu, id, c)
|
|
|
|
free(c->priv);
|
|
|
|
|
|
|
|
cu__for_each_function(cu, id, f)
|
|
|
|
free(f->priv);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-12 14:09:15 +01:00
|
|
|
static void print_total_function_diff(const char *filename)
|
|
|
|
{
|
|
|
|
printf("\n%s:\n", filename);
|
|
|
|
|
|
|
|
printf(" %u function%s changed", total_nr_functions_changed,
|
|
|
|
total_nr_functions_changed > 1 ? "s" : "");
|
|
|
|
|
2008-01-04 00:33:02 +01:00
|
|
|
if (total_function_bytes_added != 0)
|
2007-01-29 13:42:03 +01:00
|
|
|
printf(", %u bytes added", total_function_bytes_added);
|
2006-11-12 14:09:15 +01:00
|
|
|
|
2008-01-04 00:33:02 +01:00
|
|
|
if (total_function_bytes_removed != 0)
|
2007-01-29 13:42:03 +01:00
|
|
|
printf(", %u bytes removed", total_function_bytes_removed);
|
2009-03-14 17:50:36 +01:00
|
|
|
|
2008-01-04 00:33:02 +01:00
|
|
|
printf(", diff: %+d",
|
|
|
|
(total_function_bytes_added -
|
|
|
|
total_function_bytes_removed));
|
2006-11-12 14:09:15 +01:00
|
|
|
putchar('\n');
|
|
|
|
}
|
|
|
|
|
2009-02-13 13:57:23 +01:00
|
|
|
/* Name and version of program. */
|
|
|
|
ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
|
|
|
|
|
2007-03-30 15:37:37 +02:00
|
|
|
static const struct argp_option codiff__options[] = {
|
|
|
|
{
|
|
|
|
.key = 's',
|
|
|
|
.name = "structs",
|
|
|
|
.doc = "show struct diffs",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.key = 'f',
|
|
|
|
.name = "functions",
|
|
|
|
.doc = "show function diffs",
|
|
|
|
},
|
2009-04-01 19:02:21 +02:00
|
|
|
{
|
|
|
|
.name = "format_path",
|
|
|
|
.key = 'F',
|
|
|
|
.arg = "FORMAT_LIST",
|
|
|
|
.doc = "List of debugging formats to try"
|
|
|
|
},
|
2007-03-30 15:37:37 +02:00
|
|
|
{
|
|
|
|
.key = 't',
|
|
|
|
.name = "terse_type_changes",
|
|
|
|
.doc = "show terse type changes",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.key = 'V',
|
|
|
|
.name = "verbose",
|
|
|
|
.doc = "show diffs details",
|
|
|
|
},
|
2019-04-09 21:46:49 +02:00
|
|
|
{
|
|
|
|
.key = 'q',
|
|
|
|
.name = "quiet",
|
|
|
|
.doc = "Show only differences, no difference? No output",
|
|
|
|
},
|
2007-03-30 15:37:37 +02:00
|
|
|
{
|
|
|
|
.name = NULL,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static error_t codiff__options_parser(int key, char *arg __unused,
|
|
|
|
struct argp_state *state __unused)
|
|
|
|
{
|
|
|
|
switch (key) {
|
|
|
|
case 'f': show_function_diffs = 1; break;
|
2009-04-01 19:02:21 +02:00
|
|
|
case 'F': conf_load.format_path = arg; break;
|
2007-03-30 15:37:37 +02:00
|
|
|
case 's': show_struct_diffs = 1; break;
|
|
|
|
case 't': show_terse_type_changes = 1; break;
|
|
|
|
case 'V': verbose = 1; break;
|
2019-04-09 21:46:49 +02:00
|
|
|
case 'q': quiet = 1; break;
|
2007-03-30 15:37:37 +02:00
|
|
|
default: return ARGP_ERR_UNKNOWN;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-31 12:30:55 +01:00
|
|
|
static const char codiff__args_doc[] = "OLD_FILE NEW_FILE";
|
2007-03-30 15:37:37 +02:00
|
|
|
|
|
|
|
static struct argp codiff__argp = {
|
|
|
|
.options = codiff__options,
|
|
|
|
.parser = codiff__options_parser,
|
|
|
|
.args_doc = codiff__args_doc,
|
|
|
|
};
|
|
|
|
|
2006-11-11 19:31:04 +01:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2009-03-11 16:31:17 +01:00
|
|
|
int remaining, err, rc = EXIT_FAILURE;
|
2007-04-30 20:44:11 +02:00
|
|
|
char *old_filename, *new_filename;
|
2008-01-31 13:05:02 +01:00
|
|
|
struct stat st;
|
2006-11-11 19:31:04 +01:00
|
|
|
|
2009-02-10 00:43:56 +01:00
|
|
|
if (argp_parse(&codiff__argp, argc, argv, 0, &remaining, NULL) ||
|
|
|
|
remaining < argc) {
|
2007-03-30 15:37:37 +02:00
|
|
|
switch (argc - remaining) {
|
|
|
|
case 2: old_filename = argv[remaining++];
|
|
|
|
new_filename = argv[remaining++]; break;
|
2006-11-11 19:31:04 +01:00
|
|
|
case 1:
|
2007-03-30 15:37:37 +02:00
|
|
|
default: goto failure;
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
|
|
|
} else {
|
2007-03-30 15:37:37 +02:00
|
|
|
failure:
|
2009-02-10 00:43:56 +01:00
|
|
|
argp_help(&codiff__argp, stderr, ARGP_HELP_SEE, argv[0]);
|
2009-03-11 16:31:17 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dwarves__init(0)) {
|
|
|
|
fputs("codiff: insufficient memory\n", stderr);
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
goto out;
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
|
|
|
|
[CODIFF]: Terse mode
Well, needs to be a bit more terse, as prints the types per CU, i.e. for
types defined in multiple CUs we get them repeated, but using grep +
sort -u does the trick.
[acme@newtoy pahole]$ codiff -t /tmp/ipv6.ko.before /tmp/ipv6.ko.after | grep ^struct | sort -u
struct inet_connection_sock: size, offset
struct inet_sock: size, nr_members, offset
struct proto: nr_members, type
struct raw6_sock: size, offset
struct tcp6_sock: size
struct tcp_sock: size, offset
struct udp_sock: size, offset
[acme@newtoy pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
2006-11-12 19:46:11 +01:00
|
|
|
if (show_function_diffs == 0 && show_struct_diffs == 0 &&
|
|
|
|
show_terse_type_changes == 0)
|
2006-11-12 15:41:44 +01:00
|
|
|
show_function_diffs = show_struct_diffs = 1;
|
|
|
|
|
2007-12-17 14:27:24 +01:00
|
|
|
structs_printed = strlist__new(false);
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
struct cus *old_cus = cus__new(),
|
|
|
|
*new_cus = cus__new();
|
2007-12-17 14:27:24 +01:00
|
|
|
if (old_cus == NULL || new_cus == NULL || structs_printed == NULL) {
|
2006-11-11 19:31:04 +01:00
|
|
|
fputs("codiff: insufficient memory\n", stderr);
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
goto out_cus_delete;
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
|
|
|
|
2008-01-31 13:05:02 +01:00
|
|
|
if (stat(old_filename, &st) != 0) {
|
|
|
|
fprintf(stderr, "codiff: %s (%s)\n", strerror(errno), old_filename);
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
goto out_cus_delete;
|
2008-01-31 13:05:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If old_file is a character device, leave its cus empty */
|
|
|
|
if (!S_ISCHR(st.st_mode)) {
|
2009-04-01 19:02:21 +02:00
|
|
|
err = cus__load_file(old_cus, &conf_load, old_filename);
|
2008-01-31 13:05:02 +01:00
|
|
|
if (err != 0) {
|
2008-05-12 20:25:45 +02:00
|
|
|
cus__print_error_msg("codiff", old_cus, old_filename, err);
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
goto out_cus_delete_priv;
|
2008-01-31 13:05:02 +01:00
|
|
|
}
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
|
|
|
|
2008-01-31 13:05:02 +01:00
|
|
|
if (stat(new_filename, &st) != 0) {
|
|
|
|
fprintf(stderr, "codiff: %s (%s)\n", strerror(errno), new_filename);
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
goto out_cus_delete_priv;
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|
|
|
|
|
2008-01-31 13:05:02 +01:00
|
|
|
/* If old_file is a character device, leave its cus empty */
|
|
|
|
if (!S_ISCHR(st.st_mode)) {
|
2009-04-01 19:02:21 +02:00
|
|
|
err = cus__load_file(new_cus, &conf_load, new_filename);
|
2008-01-31 13:05:02 +01:00
|
|
|
if (err != 0) {
|
2008-05-12 20:25:45 +02:00
|
|
|
cus__print_error_msg("codiff", new_cus, new_filename, err);
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
goto out_cus_delete_priv;
|
2008-01-31 13:05:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-01 02:48:34 +01:00
|
|
|
cus__for_each_cu(old_cus, cu_diff_iterator, new_cus, NULL);
|
2007-01-04 00:29:24 +01:00
|
|
|
cus__for_each_cu(new_cus, cu_find_new_tags_iterator, old_cus, NULL);
|
2006-12-01 02:48:34 +01:00
|
|
|
cus__for_each_cu(old_cus, cu_show_diffs_iterator, NULL, NULL);
|
2019-04-11 18:54:47 +02:00
|
|
|
if (new_cus->nr_entries > 1)
|
|
|
|
cus__for_each_cu(new_cus, cu_show_diffs_iterator, (void *)1, NULL);
|
2006-11-11 19:31:04 +01:00
|
|
|
|
2006-11-12 15:41:44 +01:00
|
|
|
if (total_cus_changed > 1) {
|
|
|
|
if (show_function_diffs)
|
|
|
|
print_total_function_diff(new_filename);
|
|
|
|
}
|
2006-11-12 02:14:02 +01:00
|
|
|
|
2009-03-11 16:31:17 +01:00
|
|
|
rc = EXIT_SUCCESS;
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
out_cus_delete_priv:
|
2009-03-11 16:31:17 +01:00
|
|
|
cus__for_each_cu(old_cus, cu_delete_priv, NULL, NULL);
|
|
|
|
cus__for_each_cu(new_cus, cu_delete_priv, NULL, NULL);
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
out_cus_delete:
|
2009-03-11 16:31:17 +01:00
|
|
|
cus__delete(old_cus);
|
|
|
|
cus__delete(new_cus);
|
|
|
|
strlist__delete(structs_printed);
|
|
|
|
dwarves__exit();
|
all: Fix possible uninitialized variable uses
I wasn't especifying the optimization level and the default, despite
using -Wall, was for this so simple case not to be warned about, so
now I'm using -O2.
Alexandre provided a patch initializing the variables to NULL, so that
when we called cus__delete it would bail out and not possibly act on
a random value, I preferred to add extra goto labels and do the exit
path only on the resources that were successfully allocated/initialized,
avoiding, for instance, to call dwarves_exit() if dwarves_init() wasn't
called, which wasn't a problem so far, but could be in the future.
Reported-by: Alexandre Vassalotti <alexandre@peadrop.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-05-04 20:50:06 +02:00
|
|
|
out:
|
2009-03-11 16:31:17 +01:00
|
|
|
return rc;
|
2006-11-11 19:31:04 +01:00
|
|
|
}
|