pahole: Describe expected use of 'default' in the man page
[dwarves.git] / codiff.c
blob9e5c56546c314231ca08326df0b456e3abfc0325
1 /*
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>
6 */
8 #include <argp.h>
9 #include <assert.h>
10 #include <dwarf.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
18 #include "dwarves.h"
19 #include "dutil.h"
21 static int show_struct_diffs;
22 static int show_function_diffs;
23 static int verbose;
24 static int quiet;
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;
50 struct diff_info {
51 const struct tag *tag;
52 const struct cu *cu;
53 int32_t diff;
56 static struct diff_info *diff_info__new(const struct tag *twin,
57 const struct cu *cu,
58 int32_t diff)
60 struct diff_info *dinfo = malloc(sizeof(*dinfo));
62 if (dinfo == NULL) {
63 puts("out of memory!");
64 exit(1);
66 dinfo->tag = twin;
67 dinfo->cu = cu;
68 dinfo->diff = diff;
69 return dinfo;
72 static void cu__check_max_len_changed_item(struct cu *cu, const char *name,
73 uint8_t addend)
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,
82 struct cu *cu)
84 struct tag *new_tag;
85 const char *name;
87 if (function->inlined || function->abstract_origin != 0)
88 return;
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));
96 if (diff != 0) {
97 function->priv = diff_info__new(&new_function->proto.tag, new_cu,
98 diff);
99 cu__check_max_len_changed_item(cu, name, 0);
101 ++cu->nr_functions_changed;
102 if (diff > 0)
103 cu->function_bytes_added += diff;
104 else
105 cu->function_bytes_removed += -diff;
106 } else {
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,
112 twin_proto,
113 sizeof(twin_proto))) != 0) {
114 ++cu->nr_functions_changed;
115 function->priv = diff_info__new(function__tag(new_function),
116 new_cu, 0);
119 } else {
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,
133 int print)
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);
139 int changes = 0;
141 if (old_type == NULL || new_type == NULL)
142 return 0;
144 old_size = old->byte_size;
145 new_size = new->byte_size;
146 if (old_size != new_size)
147 changes = 1;
149 if (old->byte_offset != new->byte_offset) {
150 changes = 1;
151 terse_type_changes |= TCHANGEF__OFFSET;
154 if (old->bitfield_offset != new->bitfield_offset) {
155 changes = 1;
156 terse_type_changes |= TCHANGEF__BIT_OFFSET;
159 if (old->bitfield_size != new->bitfield_size) {
160 changes = 1;
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) {
168 changes = 1;
169 terse_type_changes |= TCHANGEF__TYPE;
172 if (changes && print && !show_terse_type_changes)
173 printf(" %s\n"
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);
182 return changes;
185 static struct class_member *class__find_pair_member(const struct class *structure,
186 const struct class_member *pair_member,
187 int *nr_anonymousp)
189 const char *member_name = class_member__name(pair_member);
190 struct class_member *member;
192 if (member_name)
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? */
202 --nr_anonymous == 0)
203 return member;
206 return NULL;
209 static int check_print_members_changes(const struct class *structure,
210 const struct cu *cu,
211 const struct class *new_structure,
212 const struct cu *new_cu,
213 int print)
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);
221 if (twin != NULL) {
222 twin->tag.visited = 1;
223 ++nr_twins_found;
224 if (check_print_change(member, cu, twin, new_cu, print))
225 changes = 1;
226 } else {
227 changes = 1;
228 if (print) {
229 char name[128];
230 struct tag *type;
231 type = cu__type(cu, member->tag.type);
232 printf(" %s\n"
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))
244 goto out;
246 changes = 1;
247 if (!print)
248 goto out;
250 type__for_each_member(&new_structure->type, member) {
251 if (!member->tag.visited) {
252 char name[128];
253 struct tag *type;
254 type = cu__type(new_cu, member->tag.type);
255 printf(" %s\n"
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);
263 out:
264 return changes;
267 static void diff_struct(const struct cu *new_cu, struct class *structure,
268 struct cu *cu)
270 struct tag *new_tag;
271 struct class *new_structure = NULL;
272 int32_t diff;
274 assert(class__is_struct(structure));
276 if (class__size(structure) == 0 || class__name(structure) == NULL)
277 return;
279 new_tag = cu__find_struct_by_name(new_cu, class__name(structure), 0, NULL);
280 if (new_tag == NULL)
281 return;
283 new_structure = tag__class(new_tag);
284 if (class__size(new_structure) == 0)
285 return;
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;
297 if (diff == 0)
298 return;
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),
303 new_cu, diff);
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))
311 return 0;
313 struct function *function;
314 uint32_t id;
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)
321 continue;
323 const char *name = function__name(function);
324 struct tag *old_function = cu__find_function_by_name(old_cu,
325 name);
326 if (old_function != NULL && !tag__function(old_function)->inlined)
327 continue;
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);
337 struct class *class;
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))
342 continue;
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"));
350 return 0;
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))
358 return 0;
360 uint32_t id;
361 struct class *class;
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);
369 return 0;
372 static void show_diffs_function(struct function *function, const struct cu *cu,
373 const void *cookie)
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);
381 if (!verbose) {
382 putchar('\n');
383 return;
386 if (di->tag == NULL)
387 puts(cookie ? " (added)" : " (removed)");
388 else {
389 struct function *twin = tag__function(di->tag);
391 if (twin->inlined)
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));
399 else {
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);
425 putchar('\n');
430 static void show_changed_member(char change, const struct class_member *member,
431 const struct cu *cu)
433 const struct tag *type = cu__type(cu, member->tag.type);
434 char bf[128];
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,
444 const struct cu *cu,
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);
454 if (twin == NULL)
455 show_changed_member('-', member, cu);
458 nr_anonymous = 0;
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);
462 if (twin == NULL)
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);
475 sep = ", ";
477 if (terse_type_changes & TCHANGEF__NR_MEMBERS) {
478 printf("%snr_members", sep);
479 sep = ", ";
481 if (terse_type_changes & TCHANGEF__TYPE) {
482 printf("%stype", sep);
483 sep = ", ";
485 if (terse_type_changes & TCHANGEF__OFFSET) {
486 printf("%soffset", sep);
487 sep = ", ";
489 if (terse_type_changes & TCHANGEF__BIT_OFFSET) {
490 printf("%sbit_offset", sep);
491 sep = ", ";
493 if (terse_type_changes & TCHANGEF__BIT_SIZE) {
494 printf("%sbit_size", sep);
495 sep = ", ";
497 if (terse_type_changes & TCHANGEF__PADDING) {
498 printf("%spadding", sep);
499 sep = ", ";
501 if (terse_type_changes & TCHANGEF__NR_HOLES) {
502 printf("%snr_holes", sep);
503 sep = ", ";
505 if (terse_type_changes & TCHANGEF__NR_BIT_HOLES)
506 printf("%snr_bit_holes", sep);
508 putchar('\n');
511 static void show_diffs_structure(struct class *structure,
512 const struct cu *cu)
514 const struct diff_info *di = structure->priv;
515 const struct class *new_structure;
516 int diff;
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
520 * removed.
522 if (di == NULL)
523 return;
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);
532 else
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);
543 if (diff != 0)
544 terse_type_changes |= TCHANGEF__SIZE;
546 if (!verbose && !show_terse_type_changes)
547 return;
549 if (new_structure == NULL)
550 diff = -class__nr_members(structure);
551 else
552 diff = (class__nr_members(new_structure) -
553 class__nr_members(structure));
554 if (diff != 0) {
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;
565 if (diff) {
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;
571 if (diff) {
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)
607 return 0;
609 if (first_cu_printed) {
610 if (!quiet)
611 putchar('\n');
612 } else {
613 first_cu_printed = 1;
616 ++total_cus_changed;
618 if (!quiet)
619 printf("%s:\n", cu->name);
621 uint32_t id;
622 struct class *class;
624 if (show_terse_type_changes) {
625 cu__for_each_struct(cu, id, class)
626 show_structure_diffs_iterator(class, cu);
627 return 0;
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);
659 putchar('\n');
661 return 0;
664 static int cu_delete_priv(struct cu *cu, void *cookie __maybe_unused)
666 struct class *c;
667 struct function *f;
668 uint32_t id;
670 cu__for_each_struct(cu, id, c)
671 zfree(&c->priv);
673 cu__for_each_function(cu, id, f)
674 zfree(&f->priv);
676 return 0;
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));
695 putchar('\n');
698 /* Name and version of program. */
699 ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
701 static const struct argp_option codiff__options[] = {
703 .key = 's',
704 .name = "structs",
705 .doc = "show struct diffs",
708 .key = 'f',
709 .name = "functions",
710 .doc = "show function diffs",
713 .name = "format_path",
714 .key = 'F',
715 .arg = "FORMAT_LIST",
716 .doc = "List of debugging formats to try"
719 .key = 't',
720 .name = "terse_type_changes",
721 .doc = "show terse type changes",
724 .key = 'V',
725 .name = "verbose",
726 .doc = "show diffs details",
729 .key = 'q',
730 .name = "quiet",
731 .doc = "Show only differences, no difference? No output",
734 .name = NULL,
738 static error_t codiff__options_parser(int key, char *arg __maybe_unused,
739 struct argp_state *state __maybe_unused)
741 switch (key) {
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;
750 return 0;
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;
765 struct stat st;
767 if (argp_parse(&codiff__argp, argc, argv, 0, &remaining, NULL) ||
768 remaining < argc) {
769 switch (argc - remaining) {
770 case 2: old_filename = argv[remaining++];
771 new_filename = argv[remaining++]; break;
772 case 1:
773 default: goto failure;
775 } else {
776 failure:
777 argp_help(&codiff__argp, stderr, ARGP_HELP_SEE, argv[0]);
778 goto out;
781 if (dwarves__init()) {
782 fputs("codiff: insufficient memory\n", stderr);
783 goto out;
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);
797 goto out_cus_delete;
800 if (stat(old_filename, &st) != 0) {
801 fprintf(stderr, "codiff: %s (%s)\n", strerror(errno), old_filename);
802 goto out_cus_delete;
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);
808 if (err < 0) {
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);
822 if (err < 0) {
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);
839 rc = EXIT_SUCCESS;
840 out_cus_delete_priv:
841 cus__for_each_cu(old_cus, cu_delete_priv, NULL, NULL);
842 cus__for_each_cu(new_cus, cu_delete_priv, NULL, NULL);
843 out_cus_delete:
844 cus__delete(old_cus);
845 cus__delete(new_cus);
846 strlist__delete(structs_printed);
847 dwarves__exit();
848 out:
849 return rc;