2 SPDX-License-Identifier: GPL-2.0-only
4 Copyright (C) 2006 Mandriva Conectiva S.A.
5 Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
14 #include <sys/types.h>
21 static int show_struct_diffs
;
22 static int show_function_diffs
;
25 static int show_terse_type_changes
;
27 static struct conf_load conf_load
= {
28 .get_addr_info
= true,
31 static struct strlist
*structs_printed
;
33 #define TCHANGEF__SIZE (1 << 0)
34 #define TCHANGEF__NR_MEMBERS (1 << 1)
35 #define TCHANGEF__TYPE (1 << 2)
36 #define TCHANGEF__OFFSET (1 << 3)
37 #define TCHANGEF__BIT_OFFSET (1 << 4)
38 #define TCHANGEF__BIT_SIZE (1 << 5)
39 #define TCHANGEF__PADDING (1 << 6)
40 #define TCHANGEF__NR_HOLES (1 << 7)
41 #define TCHANGEF__NR_BIT_HOLES (1 << 8)
43 static uint32_t terse_type_changes
;
45 static uint32_t total_cus_changed
;
46 static uint32_t total_nr_functions_changed
;
47 static uint32_t total_function_bytes_added
;
48 static uint32_t total_function_bytes_removed
;
51 const struct tag
*tag
;
56 static struct diff_info
*diff_info__new(const struct tag
*twin
,
60 struct diff_info
*dinfo
= malloc(sizeof(*dinfo
));
63 puts("out of memory!");
72 static void cu__check_max_len_changed_item(struct cu
*cu
, const char *name
,
75 const uint32_t len
= strlen(name
) + addend
;
77 if (len
> cu
->max_len_changed_item
)
78 cu
->max_len_changed_item
= len
;
81 static void diff_function(const struct cu
*new_cu
, struct function
*function
,
87 if (function
->inlined
|| function
->abstract_origin
!= 0)
90 name
= function__name(function
);
91 new_tag
= cu__find_function_by_name(new_cu
, name
);
92 if (new_tag
!= NULL
) {
93 struct function
*new_function
= tag__function(new_tag
);
94 int32_t diff
= (function__size(new_function
) -
95 function__size(function
));
97 function
->priv
= diff_info__new(&new_function
->proto
.tag
, new_cu
,
99 cu__check_max_len_changed_item(cu
, name
, 0);
101 ++cu
->nr_functions_changed
;
103 cu
->function_bytes_added
+= diff
;
105 cu
->function_bytes_removed
+= -diff
;
107 char proto
[1024], twin_proto
[1024];
109 if (strcmp(function__prototype(function
, cu
,
110 proto
, sizeof(proto
)),
111 function__prototype(new_function
, new_cu
,
113 sizeof(twin_proto
))) != 0) {
114 ++cu
->nr_functions_changed
;
115 function
->priv
= diff_info__new(function__tag(new_function
),
120 const uint32_t diff
= -function__size(function
);
122 cu__check_max_len_changed_item(cu
, name
, 0);
123 function
->priv
= diff_info__new(NULL
, NULL
, diff
);
124 ++cu
->nr_functions_changed
;
125 cu
->function_bytes_removed
+= -diff
;
129 static int check_print_change(const struct class_member
*old
,
130 const struct cu
*old_cu
,
131 const struct class_member
*new,
132 const struct cu
*new_cu
,
135 size_t old_size
, new_size
;
136 char old_type_name
[128], new_type_name
[128];
137 const struct tag
*old_type
= cu__type(old_cu
, old
->tag
.type
);
138 const struct tag
*new_type
= cu__type(new_cu
, new->tag
.type
);
141 if (old_type
== NULL
|| new_type
== NULL
)
144 old_size
= old
->byte_size
;
145 new_size
= new->byte_size
;
146 if (old_size
!= new_size
)
149 if (old
->byte_offset
!= new->byte_offset
) {
151 terse_type_changes
|= TCHANGEF__OFFSET
;
154 if (old
->bitfield_offset
!= new->bitfield_offset
) {
156 terse_type_changes
|= TCHANGEF__BIT_OFFSET
;
159 if (old
->bitfield_size
!= new->bitfield_size
) {
161 terse_type_changes
|= TCHANGEF__BIT_SIZE
;
164 if (strcmp(tag__name(old_type
, old_cu
, old_type_name
,
165 sizeof(old_type_name
), NULL
),
166 tag__name(new_type
, new_cu
, new_type_name
,
167 sizeof(new_type_name
), NULL
)) != 0) {
169 terse_type_changes
|= TCHANGEF__TYPE
;
172 if (changes
&& print
&& !show_terse_type_changes
)
174 " from: %-21s /* %5u(%2u) %5zd(%2d) */\n"
175 " to: %-21s /* %5u(%2u) %5zd(%2u) */\n",
176 class_member__name(old
),
177 old_type_name
, old
->byte_offset
, old
->bitfield_offset
,
178 old_size
, old
->bitfield_size
,
179 new_type_name
, new->byte_offset
, new->bitfield_offset
,
180 new_size
, new->bitfield_size
);
185 static struct class_member
*class__find_pair_member(const struct class *structure
,
186 const struct class_member
*pair_member
,
189 const char *member_name
= class_member__name(pair_member
);
190 struct class_member
*member
;
193 return class__find_member_by_name(structure
, member_name
);
195 int nr_anonymous
= ++*nr_anonymousp
;
197 /* Unnamed struct or union, lets look for the first unammed matchin tag.type */
199 type__for_each_member(&structure
->type
, member
) {
200 if (member
->tag
.tag
== pair_member
->tag
.tag
&& /* Both are class/union/struct (unnamed) */
201 class_member__name(member
) == member_name
&& /* Both are NULL? */
209 static int check_print_members_changes(const struct class *structure
,
211 const struct class *new_structure
,
212 const struct cu
*new_cu
,
215 int changes
= 0, nr_anonymous
= 0;
216 struct class_member
*member
;
217 uint16_t nr_twins_found
= 0;
219 type__for_each_member(&structure
->type
, member
) {
220 struct class_member
*twin
= class__find_pair_member(new_structure
, member
, &nr_anonymous
);
222 twin
->tag
.visited
= 1;
224 if (check_print_change(member
, cu
, twin
, new_cu
, print
))
231 type
= cu__type(cu
, member
->tag
.type
);
233 " removed: %-21s /* %5u(%2u) %5zd(%2d) */\n",
234 class_member__name(member
),
235 tag__name(type
, cu
, name
, sizeof(name
), NULL
),
236 member
->byte_offset
, member
->bitfield_offset
,
237 member
->byte_size
, member
->bitfield_size
);
242 if (nr_twins_found
== (new_structure
->type
.nr_members
+
243 new_structure
->type
.nr_static_members
))
250 type__for_each_member(&new_structure
->type
, member
) {
251 if (!member
->tag
.visited
) {
254 type
= cu__type(new_cu
, member
->tag
.type
);
256 " added: %-21s /* %5u(%2u) %5zd(%2d) */\n",
257 class_member__name(member
),
258 tag__name(type
, new_cu
, name
, sizeof(name
), NULL
),
259 member
->byte_offset
, member
->bitfield_offset
,
260 member
->byte_size
, member
->bitfield_size
);
267 static void diff_struct(const struct cu
*new_cu
, struct class *structure
,
271 struct class *new_structure
= NULL
;
274 assert(class__is_struct(structure
));
276 if (class__size(structure
) == 0 || class__name(structure
) == NULL
)
279 new_tag
= cu__find_struct_by_name(new_cu
, class__name(structure
), 0, NULL
);
283 new_structure
= tag__class(new_tag
);
284 if (class__size(new_structure
) == 0)
287 assert(class__is_struct(new_structure
));
289 diff
= class__size(structure
) != class__size(new_structure
) ||
290 class__nr_members(structure
) != class__nr_members(new_structure
) ||
291 check_print_members_changes(structure
, cu
,
292 new_structure
, new_cu
, 0) ||
293 structure
->padding
!= new_structure
->padding
||
294 structure
->nr_holes
!= new_structure
->nr_holes
||
295 structure
->nr_bit_holes
!= new_structure
->nr_bit_holes
;
300 ++cu
->nr_structures_changed
;
301 cu__check_max_len_changed_item(cu
, class__name(structure
), sizeof("struct"));
302 structure
->priv
= diff_info__new(class__tag(new_structure
),
306 static int cu_find_new_tags_iterator(struct cu
*new_cu
, void *old_cus
)
308 struct cu
*old_cu
= cus__find_pair(old_cus
, new_cu
->name
);
310 if (old_cu
!= NULL
&& cu__same_build_id(old_cu
, new_cu
))
313 struct function
*function
;
315 cu__for_each_function(new_cu
, id
, function
) {
317 * We're not interested in aliases, just real function definitions,
318 * where we'll know if the kind of inlining
320 if (function
->abstract_origin
|| function
->inlined
)
323 const char *name
= function__name(function
);
324 struct tag
*old_function
= cu__find_function_by_name(old_cu
,
326 if (old_function
!= NULL
&& !tag__function(old_function
)->inlined
)
329 const int32_t diff
= function__size(function
);
331 cu__check_max_len_changed_item(new_cu
, name
, 0);
332 ++new_cu
->nr_functions_changed
;
333 new_cu
->function_bytes_added
+= diff
;
334 function
->priv
= diff_info__new(old_function
, new_cu
, diff
);
338 cu__for_each_struct(new_cu
, id
, class) {
339 const char *name
= class__name(class);
340 if (name
== NULL
|| class__size(class) == 0 ||
341 cu__find_struct_by_name(old_cu
, name
, 0, NULL
))
344 class->priv
= diff_info__new(NULL
, NULL
, 1);
345 ++new_cu
->nr_structures_changed
;
347 cu__check_max_len_changed_item(new_cu
, name
, sizeof("struct"));
353 static int cu_diff_iterator(struct cu
*cu
, void *new_cus
)
355 struct cu
*new_cu
= cus__find_pair(new_cus
, cu
->name
);
357 if (new_cu
!= NULL
&& cu__same_build_id(cu
, new_cu
))
362 cu__for_each_struct(cu
, id
, class)
363 diff_struct(new_cu
, class, cu
);
365 struct function
*function
;
366 cu__for_each_function(cu
, id
, function
)
367 diff_function(new_cu
, function
, cu
);
372 static void show_diffs_function(struct function
*function
, const struct cu
*cu
,
375 const struct diff_info
*di
= function
->priv
;
377 printf(" %-*.*s | %+4d",
378 (int)cu
->max_len_changed_item
, (int)cu
->max_len_changed_item
,
379 function__name(function
), di
->diff
);
387 puts(cookie
? " (added)" : " (removed)");
389 struct function
*twin
= tag__function(di
->tag
);
392 puts(cookie
? " (uninlined)" : " (inlined)");
393 else if (strcmp(function__name(function
),
394 function__name(twin
)) != 0)
395 printf("%s: BRAIN FART ALERT: comparing %s to %s, "
396 "should be the same name\n", __FUNCTION__
,
397 function__name(function
),
398 function__name(twin
));
400 char proto
[1024], twin_proto
[1024];
402 printf(" # %d -> %d", function__size(function
),
403 function__size(twin
));
404 if (function
->lexblock
.nr_lexblocks
!=
405 twin
->lexblock
.nr_lexblocks
)
406 printf(", lexblocks: %d -> %d",
407 function
->lexblock
.nr_lexblocks
,
408 twin
->lexblock
.nr_lexblocks
);
409 if (function
->lexblock
.nr_inline_expansions
!=
410 twin
->lexblock
.nr_inline_expansions
)
411 printf(", # inlines: %d -> %d",
412 function
->lexblock
.nr_inline_expansions
,
413 twin
->lexblock
.nr_inline_expansions
);
414 if (function
->lexblock
.size_inline_expansions
!=
415 twin
->lexblock
.size_inline_expansions
)
416 printf(", size inlines: %d -> %d",
417 function
->lexblock
.size_inline_expansions
,
418 twin
->lexblock
.size_inline_expansions
);
420 if (strcmp(function__prototype(function
, cu
,
421 proto
, sizeof(proto
)),
422 function__prototype(twin
, di
->cu
,
423 twin_proto
, sizeof(twin_proto
))) != 0)
424 printf(", prototype: %s -> %s", proto
, twin_proto
);
430 static void show_changed_member(char change
, const struct class_member
*member
,
433 const struct tag
*type
= cu__type(cu
, member
->tag
.type
);
436 tag__assert_search_result(type
, member
->tag
.tag
, class_member__name(member
));
437 printf(" %c%-26s %-21s /* %5u %5zd */\n",
438 change
, tag__name(type
, cu
, bf
, sizeof(bf
), NULL
),
439 class_member__name(member
),
440 member
->byte_offset
, member
->byte_size
);
443 static void show_nr_members_changes(const struct class *structure
,
445 const struct class *new_structure
,
446 const struct cu
*new_cu
)
448 struct class_member
*member
;
449 int nr_anonymous
= 0;
451 /* Find the removed ones */
452 type__for_each_member(&structure
->type
, member
) {
453 struct class_member
*twin
= class__find_pair_member(new_structure
, member
, &nr_anonymous
);
455 show_changed_member('-', member
, cu
);
459 /* Find the new ones */
460 type__for_each_member(&new_structure
->type
, member
) {
461 struct class_member
*twin
= class__find_pair_member(structure
, member
, &nr_anonymous
);
463 show_changed_member('+', member
, new_cu
);
467 static void print_terse_type_changes(struct class *structure
)
469 const char *sep
= "";
471 printf("struct %s: ", class__name(structure
));
473 if (terse_type_changes
& TCHANGEF__SIZE
) {
474 fputs("size", stdout
);
477 if (terse_type_changes
& TCHANGEF__NR_MEMBERS
) {
478 printf("%snr_members", sep
);
481 if (terse_type_changes
& TCHANGEF__TYPE
) {
482 printf("%stype", sep
);
485 if (terse_type_changes
& TCHANGEF__OFFSET
) {
486 printf("%soffset", sep
);
489 if (terse_type_changes
& TCHANGEF__BIT_OFFSET
) {
490 printf("%sbit_offset", sep
);
493 if (terse_type_changes
& TCHANGEF__BIT_SIZE
) {
494 printf("%sbit_size", sep
);
497 if (terse_type_changes
& TCHANGEF__PADDING
) {
498 printf("%spadding", sep
);
501 if (terse_type_changes
& TCHANGEF__NR_HOLES
) {
502 printf("%snr_holes", sep
);
505 if (terse_type_changes
& TCHANGEF__NR_BIT_HOLES
)
506 printf("%snr_bit_holes", sep
);
511 static void show_diffs_structure(struct class *structure
,
514 const struct diff_info
*di
= structure
->priv
;
515 const struct class *new_structure
;
518 * This is when the struct was not present in the new object file.
519 * Meaning that it either was not referenced or that it was completely
525 new_structure
= tag__class(di
->tag
);
527 * If there is a diff_info but its di->tag is NULL we have a new structure,
528 * one that didn't appears in the old object. See find_new_classes_iterator.
530 if (new_structure
== NULL
)
531 diff
= class__size(structure
);
533 diff
= class__size(new_structure
) - class__size(structure
);
535 terse_type_changes
= 0;
537 if (!show_terse_type_changes
)
538 printf(" struct %-*.*s | %+4d\n",
539 (int)(cu
->max_len_changed_item
- sizeof("struct")),
540 (int)(cu
->max_len_changed_item
- sizeof("struct")),
541 class__name(structure
), diff
);
544 terse_type_changes
|= TCHANGEF__SIZE
;
546 if (!verbose
&& !show_terse_type_changes
)
549 if (new_structure
== NULL
)
550 diff
= -class__nr_members(structure
);
552 diff
= (class__nr_members(new_structure
) -
553 class__nr_members(structure
));
555 terse_type_changes
|= TCHANGEF__NR_MEMBERS
;
556 if (!show_terse_type_changes
) {
557 printf(" nr_members: %+d\n", diff
);
558 if (new_structure
!= NULL
)
559 show_nr_members_changes(structure
, cu
,
560 new_structure
, di
->cu
);
563 if (new_structure
!= NULL
) {
564 diff
= (int)new_structure
->padding
- (int)structure
->padding
;
566 terse_type_changes
|= TCHANGEF__PADDING
;
567 if (!show_terse_type_changes
)
568 printf(" padding: %+d\n", diff
);
570 diff
= (int)new_structure
->nr_holes
- (int)structure
->nr_holes
;
572 terse_type_changes
|= TCHANGEF__NR_HOLES
;
573 if (!show_terse_type_changes
)
574 printf(" nr_holes: %+d\n", diff
);
576 diff
= ((int)new_structure
->nr_bit_holes
-
577 (int)structure
->nr_bit_holes
);
578 if (structure
->nr_bit_holes
!= new_structure
->nr_bit_holes
) {
579 terse_type_changes
|= TCHANGEF__NR_BIT_HOLES
;
580 if (!show_terse_type_changes
)
581 printf(" nr_bit_holes: %+d\n", diff
);
583 check_print_members_changes(structure
, cu
,
584 new_structure
, di
->cu
, 1);
586 if (show_terse_type_changes
)
587 print_terse_type_changes(structure
);
590 static void show_structure_diffs_iterator(struct class *class, struct cu
*cu
)
592 if (class->priv
!= NULL
) {
593 const char *name
= class__name(class);
594 if (!strlist__has_entry(structs_printed
, name
)) {
595 show_diffs_structure(class, cu
);
596 strlist__add(structs_printed
, name
);
601 static int cu_show_diffs_iterator(struct cu
*cu
, void *cookie
)
603 static int first_cu_printed
;
605 if (cu
->nr_functions_changed
== 0 &&
606 cu
->nr_structures_changed
== 0)
609 if (first_cu_printed
) {
613 first_cu_printed
= 1;
619 printf("%s:\n", cu
->name
);
624 if (show_terse_type_changes
) {
625 cu__for_each_struct(cu
, id
, class)
626 show_structure_diffs_iterator(class, cu
);
630 if (cu
->nr_structures_changed
!= 0 && show_struct_diffs
) {
631 cu__for_each_struct(cu
, id
, class)
632 show_structure_diffs_iterator(class, cu
);
633 printf(" %u struct%s changed\n", cu
->nr_structures_changed
,
634 cu
->nr_structures_changed
> 1 ? "s" : "");
637 if (cu
->nr_functions_changed
!= 0 && show_function_diffs
) {
638 total_nr_functions_changed
+= cu
->nr_functions_changed
;
640 struct function
*function
;
641 cu__for_each_function(cu
, id
, function
) {
642 if (function
->priv
!= NULL
)
643 show_diffs_function(function
, cu
, cookie
);
646 printf(" %u function%s changed", cu
->nr_functions_changed
,
647 cu
->nr_functions_changed
> 1 ? "s" : "");
648 if (cu
->function_bytes_added
!= 0) {
649 total_function_bytes_added
+= cu
->function_bytes_added
;
650 printf(", %zd bytes added", cu
->function_bytes_added
);
652 if (cu
->function_bytes_removed
!= 0) {
653 total_function_bytes_removed
+= cu
->function_bytes_removed
;
654 printf(", %zd bytes removed",
655 cu
->function_bytes_removed
);
657 printf(", diff: %+zd",
658 cu
->function_bytes_added
- cu
->function_bytes_removed
);
664 static int cu_delete_priv(struct cu
*cu
, void *cookie __maybe_unused
)
670 cu__for_each_struct(cu
, id
, c
)
673 cu__for_each_function(cu
, id
, f
)
679 static void print_total_function_diff(const char *filename
)
681 printf("\n%s:\n", filename
);
683 printf(" %u function%s changed", total_nr_functions_changed
,
684 total_nr_functions_changed
> 1 ? "s" : "");
686 if (total_function_bytes_added
!= 0)
687 printf(", %u bytes added", total_function_bytes_added
);
689 if (total_function_bytes_removed
!= 0)
690 printf(", %u bytes removed", total_function_bytes_removed
);
692 printf(", diff: %+d",
693 (total_function_bytes_added
-
694 total_function_bytes_removed
));
698 /* Name and version of program. */
699 ARGP_PROGRAM_VERSION_HOOK_DEF
= dwarves_print_version
;
701 static const struct argp_option codiff__options
[] = {
705 .doc
= "show struct diffs",
710 .doc
= "show function diffs",
713 .name
= "format_path",
715 .arg
= "FORMAT_LIST",
716 .doc
= "List of debugging formats to try"
720 .name
= "terse_type_changes",
721 .doc
= "show terse type changes",
726 .doc
= "show diffs details",
731 .doc
= "Show only differences, no difference? No output",
738 static error_t
codiff__options_parser(int key
, char *arg __maybe_unused
,
739 struct argp_state
*state __maybe_unused
)
742 case 'f': show_function_diffs
= 1; break;
743 case 'F': conf_load
.format_path
= arg
; break;
744 case 's': show_struct_diffs
= 1; break;
745 case 't': show_terse_type_changes
= 1; break;
746 case 'V': verbose
= 1; break;
747 case 'q': quiet
= 1; break;
748 default: return ARGP_ERR_UNKNOWN
;
753 static const char codiff__args_doc
[] = "OLD_FILE NEW_FILE";
755 static struct argp codiff__argp
= {
756 .options
= codiff__options
,
757 .parser
= codiff__options_parser
,
758 .args_doc
= codiff__args_doc
,
761 int main(int argc
, char *argv
[])
763 int remaining
, err
, rc
= EXIT_FAILURE
;
764 char *old_filename
, *new_filename
;
767 if (argp_parse(&codiff__argp
, argc
, argv
, 0, &remaining
, NULL
) ||
769 switch (argc
- remaining
) {
770 case 2: old_filename
= argv
[remaining
++];
771 new_filename
= argv
[remaining
++]; break;
773 default: goto failure
;
777 argp_help(&codiff__argp
, stderr
, ARGP_HELP_SEE
, argv
[0]);
781 if (dwarves__init()) {
782 fputs("codiff: insufficient memory\n", stderr
);
786 dwarves__resolve_cacheline_size(&conf_load
, 0);
788 if (show_function_diffs
== 0 && show_struct_diffs
== 0 &&
789 show_terse_type_changes
== 0)
790 show_function_diffs
= show_struct_diffs
= 1;
792 structs_printed
= strlist__new(false);
793 struct cus
*old_cus
= cus__new(),
794 *new_cus
= cus__new();
795 if (old_cus
== NULL
|| new_cus
== NULL
|| structs_printed
== NULL
) {
796 fputs("codiff: insufficient memory\n", stderr
);
800 if (stat(old_filename
, &st
) != 0) {
801 fprintf(stderr
, "codiff: %s (%s)\n", strerror(errno
), old_filename
);
805 /* If old_file is a character device, leave its cus empty */
806 if (!S_ISCHR(st
.st_mode
)) {
807 err
= cus__load_file(old_cus
, &conf_load
, old_filename
);
809 cus__print_error_msg("codiff", old_cus
, old_filename
, err
);
810 goto out_cus_delete_priv
;
814 if (stat(new_filename
, &st
) != 0) {
815 fprintf(stderr
, "codiff: %s (%s)\n", strerror(errno
), new_filename
);
816 goto out_cus_delete_priv
;
819 /* If old_file is a character device, leave its cus empty */
820 if (!S_ISCHR(st
.st_mode
)) {
821 err
= cus__load_file(new_cus
, &conf_load
, new_filename
);
823 cus__print_error_msg("codiff", new_cus
, new_filename
, err
);
824 goto out_cus_delete_priv
;
828 cus__for_each_cu(old_cus
, cu_diff_iterator
, new_cus
, NULL
);
829 cus__for_each_cu(new_cus
, cu_find_new_tags_iterator
, old_cus
, NULL
);
830 cus__for_each_cu(old_cus
, cu_show_diffs_iterator
, NULL
, NULL
);
831 if (cus__nr_entries(new_cus
) > 1)
832 cus__for_each_cu(new_cus
, cu_show_diffs_iterator
, (void *)1, NULL
);
834 if (total_cus_changed
> 1) {
835 if (show_function_diffs
)
836 print_total_function_diff(new_filename
);
841 cus__for_each_cu(old_cus
, cu_delete_priv
, NULL
, NULL
);
842 cus__for_each_cu(new_cus
, cu_delete_priv
, NULL
, NULL
);
844 cus__delete(old_cus
);
845 cus__delete(new_cus
);
846 strlist__delete(structs_printed
);