1 /* -*- mode: C; c-file-style: "gnu" -*- */ 2 /* decode-gcov.c gcov decoder program 3 * 4 * Copyright (C) 2003 Red Hat Inc. 5 * 6 * Partially derived from gcov, 7 * Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 8 * 1999, 2000, 2001, 2002 Free Software Foundation, Inc. 9 * 10 * This file is NOT licensed under the Academic Free License 11 * as it is largely derived from gcov.c and gcov-io.h in the 12 * gcc source code. 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation; either version 2 of the License, or 17 * (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, write to the Free Software 26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 27 * 28 */ 29 30 #define DBUS_COMPILATION /* cheat */ 31 #include <dbus/dbus-list.h> 32 #include <dbus/dbus-string.h> 33 #include <dbus/dbus-sysdeps.h> 34 #include <dbus/dbus-marshal.h> 35 #include <dbus/dbus-hash.h> 36 #undef DBUS_COMPILATION 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #ifndef DBUS_HAVE_INT64 42 #error "gcov support can't be built without 64-bit integer support" 43 #endif 44 45 static void 46 die (const char *message) 47 { 48 fprintf (stderr, "%s", message); 49 exit (1); 50 } 51 52 /* This bizarro function is from gcov-io.h in gcc source tree */ 53 static int 54 fetch_long (long *dest, 55 const char *source, 56 size_t bytes) 57 { 58 long value = 0; 59 int i; 60 61 for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--) 62 if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 )) 63 return 1; 64 65 for (; i >= 0; i--) 66 value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255)); 67 68 if ((source[bytes - 1] & 128) && (value > 0)) 69 value = - value; 70 71 *dest = value; 72 return 0; 73 } 74 75 static int 76 fetch_long64 (dbus_int64_t *dest, 77 const char *source, 78 size_t bytes) 79 { 80 dbus_int64_t value = 0; 81 int i; 82 83 for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--) 84 if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 )) 85 return 1; 86 87 for (; i >= 0; i--) 88 value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255)); 89 90 if ((source[bytes - 1] & 128) && (value > 0)) 91 value = - value; 92 93 *dest = value; 94 return 0; 95 } 96 97 #define BB_FILENAME (-1) 98 #define BB_FUNCTION (-2) 99 #define BB_ENDOFLIST 0 100 101 static dbus_bool_t 102 string_get_int (const DBusString *str, 103 int start, 104 long *val) 105 { 106 const char *p; 107 108 if ((_dbus_string_get_length (str) - start) < 4) 109 return FALSE; 110 111 p = _dbus_string_get_const_data (str); 112 113 p += start; 114 115 fetch_long (val, p, 4); 116 117 return TRUE; 118 } 119 120 static dbus_bool_t 121 string_get_int64 (const DBusString *str, 122 int start, 123 dbus_int64_t *val) 124 { 125 const char *p; 126 127 if ((_dbus_string_get_length (str) - start) < 8) 128 return FALSE; 129 130 p = _dbus_string_get_const_data (str); 131 132 p += start; 133 134 fetch_long64 (val, p, 8); 135 136 return TRUE; 137 } 138 139 static dbus_bool_t 140 string_get_string (const DBusString *str, 141 int start, 142 long terminator, 143 DBusString *val, 144 int *end) 145 { 146 int i; 147 long n; 148 149 i = start; 150 while (string_get_int (str, i, &n)) 151 { 152 unsigned char b; 153 154 i += 4; 155 156 if (n == terminator) 157 break; 158 159 b = n & 0xff; 160 if (b) 161 { 162 _dbus_string_append_byte (val, b); 163 b = (n >> 8) & 0xff; 164 if (b) 165 { 166 _dbus_string_append_byte (val, b); 167 b = (n >> 16) & 0xff; 168 if (b) 169 { 170 _dbus_string_append_byte (val, b); 171 b = (n >> 24) & 0xff; 172 if (b) 173 _dbus_string_append_byte (val, b); 174 } 175 } 176 } 177 } 178 179 *end = i; 180 181 return TRUE; 182 } 183 184 #ifdef DBUS_HAVE_GCC33_GCOV 185 /* In gcc33 .bbg files, there's a function name of the form: 186 * -1, length, name (padded to 4), -1, checksum 187 */ 188 static dbus_bool_t 189 string_get_function (const DBusString *str, 190 int start, 191 DBusString *funcname, 192 int *checksum, 193 int *next) 194 { 195 int end; 196 long val; 197 int i; 198 199 i = start; 200 201 if (!string_get_int (str, i, &val)) 202 die ("no room for -1 before function name\n"); 203 204 i += 4; 205 206 if (val != -1) 207 die ("value before function name is not -1\n"); 208 209 if (!string_get_int (str, i, &val)) 210 die ("no length found for function name\n"); 211 212 i += 4; 213 214 end = i + val; 215 if (end > _dbus_string_get_length (str)) 216 die ("Function name length points past end of file\n"); 217 218 if (!_dbus_string_append (funcname, 219 _dbus_string_get_const_data (str) + i)) 220 die ("no memory\n"); 221 222 /* skip alignment padding the length doesn't include the nul so add 1 223 */ 224 i = _DBUS_ALIGN_VALUE (end + 1, 4); 225 226 if (!string_get_int (str, i, &val) || 227 val != -1) 228 die ("-1 at end of function name not found\n"); 229 230 i += 4; 231 232 if (!string_get_int (str, i, &val)) 233 die ("no checksum found at end of function name\n"); 234 235 i += 4; 236 237 *checksum = val; 238 239 *next = i; 240 241 return TRUE; 242 } 243 #endif /* DBUS_HAVE_GCC33_GCOV */ 244 245 static void 246 dump_bb_file (const DBusString *contents) 247 { 248 int i; 249 long val; 250 int n_functions; 251 252 n_functions = 0; 253 i = 0; 254 while (string_get_int (contents, i, &val)) 255 { 256 i += 4; 257 258 switch (val) 259 { 260 case BB_FILENAME: 261 { 262 DBusString f; 263 264 if (!_dbus_string_init (&f)) 265 die ("no memory\n"); 266 267 if (string_get_string (contents, i, 268 BB_FILENAME, 269 &f, &i)) 270 { 271 printf ("File %s\n", _dbus_string_get_const_data (&f)); 272 } 273 _dbus_string_free (&f); 274 } 275 break; 276 case BB_FUNCTION: 277 { 278 DBusString f; 279 if (!_dbus_string_init (&f)) 280 die ("no memory\n"); 281 282 if (string_get_string (contents, i, 283 BB_FUNCTION, 284 &f, &i)) 285 { 286 printf ("Function %s\n", _dbus_string_get_const_data (&f)); 287 } 288 _dbus_string_free (&f); 289 290 n_functions += 1; 291 } 292 break; 293 case BB_ENDOFLIST: 294 printf ("End of block\n"); 295 break; 296 default: 297 printf ("Line %ld\n", val); 298 break; 299 } 300 } 301 302 printf ("%d functions in file\n", n_functions); 303 } 304 305 #define FLAG_ON_TREE 0x1 306 #define FLAG_FAKE 0x2 307 #define FLAG_FALL_THROUGH 0x4 308 309 static void 310 dump_bbg_file (const DBusString *contents) 311 { 312 int i; 313 long val; 314 int n_functions; 315 int n_arcs; 316 int n_blocks; 317 int n_arcs_off_tree; 318 319 n_arcs_off_tree = 0; 320 n_blocks = 0; 321 n_arcs = 0; 322 n_functions = 0; 323 i = 0; 324 while (i < _dbus_string_get_length (contents)) 325 { 326 long n_blocks_in_func; 327 long n_arcs_in_func; 328 int j; 329 330 #ifdef DBUS_HAVE_GCC33_GCOV 331 /* In gcc33 .bbg files, there's a function name of the form: 332 * -1, length, name (padded to 4), -1, checksum 333 * after that header on each function description, it's 334 * the same as in gcc32 335 */ 336 337 { 338 DBusString funcname; 339 int checksum; 340 341 if (!_dbus_string_init (&funcname)) 342 die ("no memory\n"); 343 344 if (!string_get_function (contents, i, 345 &funcname, &checksum, &i)) 346 die ("could not read function name\n"); 347 348 printf ("Function name is \"%s\" checksum %d\n", 349 _dbus_string_get_const_data (&funcname), 350 checksum); 351 352 _dbus_string_free (&funcname); 353 } 354 #endif /* DBUS_HAVE_GCC33_GCOV */ 355 356 if (!string_get_int (contents, i, &val)) 357 die ("no count of blocks in func found\n"); 358 359 i += 4; 360 361 n_blocks_in_func = val; 362 363 if (!string_get_int (contents, i, &n_arcs_in_func)) 364 break; 365 366 i += 4; 367 368 printf ("Function has %ld blocks and %ld arcs\n", 369 n_blocks_in_func, n_arcs_in_func); 370 371 n_functions += 1; 372 n_blocks += n_blocks_in_func; 373 n_arcs += n_arcs_in_func; 374 375 j = 0; 376 while (j < n_blocks_in_func) 377 { 378 long n_arcs_in_block; 379 int k; 380 381 if (!string_get_int (contents, i, &n_arcs_in_block)) 382 break; 383 384 i += 4; 385 386 printf (" Block has %ld arcs\n", n_arcs_in_block); 387 388 k = 0; 389 while (k < n_arcs_in_block) 390 { 391 long destination_block; 392 long flags; 393 394 if (!string_get_int (contents, i, &destination_block)) 395 break; 396 397 i += 4; 398 399 if (!string_get_int (contents, i, &flags)) 400 break; 401 402 i += 4; 403 404 printf (" Arc has destination block %ld flags 0x%lx\n", 405 destination_block, flags); 406 407 if ((flags & FLAG_ON_TREE) == 0) 408 n_arcs_off_tree += 1; 409 410 ++k; 411 } 412 413 if (k < n_arcs_in_block) 414 break; 415 416 ++j; 417 } 418 419 if (j < n_blocks_in_func) 420 break; 421 422 if (!string_get_int (contents, i, &val)) 423 break; 424 425 i += 4; 426 427 if (val != -1) 428 die ("-1 separator not found\n"); 429 } 430 431 printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n", 432 n_functions, n_blocks, n_arcs, n_arcs_off_tree); 433 } 434 435 #ifndef DBUS_HAVE_GCC33_GCOV 436 437 /* gcc 3.2 version: 438 * The da file contains first a count of arcs in the file, 439 * then a count of executions for all "off tree" arcs 440 * in the file. 441 */ 442 static void 443 dump_da_file (const DBusString *contents) 444 { 445 int i; 446 dbus_int64_t val; 447 int n_arcs; 448 int claimed_n_arcs; 449 450 i = 0; 451 if (!string_get_int64 (contents, i, &val)) 452 return; 453 454 i += 8; 455 456 printf ("%ld arcs in file\n", (long) val); 457 claimed_n_arcs = val; 458 459 n_arcs = 0; 460 while (string_get_int64 (contents, i, &val)) 461 { 462 i += 8; 463 464 printf ("%ld executions of arc %d\n", 465 (long) val, n_arcs); 466 467 ++n_arcs; 468 } 469 470 if (n_arcs != claimed_n_arcs) 471 { 472 printf ("File claimed to have %d arcs but only had %d\n", 473 claimed_n_arcs, n_arcs); 474 } 475 } 476 477 #else /* DBUS_HAVE_GCC33_GCOV */ 478 479 /* gcc 3.3 version: 480 * The da file is more complex than 3.2. 481 * 482 * We have a magic value of "-123" only it isn't really 483 * -123, it's -123 as encoded by the crackass gcov-io.h 484 * routines. Anyway, 4 bytes. 485 * 486 * We then have: 487 * 488 * - 4 byte count of how many functions in the following list 489 * - 4 byte length of random extra data 490 * - the random extra data, just skip it, info pages have some 491 * details on what might be in there or see __bb_exit_func in gcc 492 * - then for each function (number of functions given above): 493 * . -1, length, funcname, alignment padding, -1 494 * . checksum 495 * . 4 byte number of arcs in function 496 * . 8 bytes each, a count of execution for each arc 497 * 498 * Now, the whole thing *starting with the magic* can repeat. 499 * This is caused by multiple runs of the profiled app appending 500 * to the file. 501 */ 502 static void 503 dump_da_file (const DBusString *contents) 504 { 505 int i; 506 dbus_int64_t v64; 507 long val; 508 int n_sections; 509 int total_functions; 510 511 total_functions = 0; 512 n_sections = 0; 513 514 i = 0; 515 while (i < _dbus_string_get_length (contents)) 516 { 517 int claimed_n_functions; 518 int n_functions; 519 int total_arcs; 520 521 printf (".da file section %d\n", n_sections); 522 523 if (!string_get_int (contents, i, &val)) 524 die ("no magic found in .da file\n"); 525 526 i += 4; 527 528 if (val != -123) 529 die ("wrong file magic in .da file\n"); 530 531 if (!string_get_int (contents, i, &val)) 532 die ("no function count in .da file\n"); 533 i += 4; 534 claimed_n_functions = val; 535 536 printf ("%d functions expected in section %d of .da file\n", 537 claimed_n_functions, n_sections); 538 539 if (!string_get_int (contents, i, &val)) 540 die ("no extra data length in .da file\n"); 541 542 i += 4; 543 544 i += val; 545 546 total_arcs = 0; 547 n_functions = 0; 548 while (n_functions < claimed_n_functions) 549 { 550 DBusString funcname; 551 int checksum; 552 int claimed_n_arcs; 553 int n_arcs; 554 555 if (!_dbus_string_init (&funcname)) 556 die ("no memory\n"); 557 558 if (!string_get_function (contents, i, 559 &funcname, &checksum, &i)) 560 die ("could not read function name\n"); 561 562 if (!string_get_int (contents, i, &val)) 563 die ("no arc count for function\n"); 564 565 i += 4; 566 claimed_n_arcs = val; 567 568 printf (" %d arcs in function %d %s checksum %d\n", 569 claimed_n_arcs, n_functions, 570 _dbus_string_get_const_data (&funcname), 571 checksum); 572 573 n_arcs = 0; 574 while (n_arcs < claimed_n_arcs) 575 { 576 if (!string_get_int64 (contents, i, &v64)) 577 die ("did not get execution count for arc\n"); 578 579 i += 8; 580 581 printf (" %ld executions of arc %d (total arcs %d)\n", 582 (long) v64, n_arcs, total_arcs + n_arcs); 583 584 ++n_arcs; 585 } 586 587 _dbus_string_free (&funcname); 588 589 total_arcs += n_arcs; 590 ++n_functions; 591 } 592 593 printf ("total of %d functions and %d arcs in section %d\n", 594 n_functions, total_arcs, n_sections); 595 596 total_functions += n_functions; 597 ++n_sections; 598 } 599 600 printf ("%d total function sections in %d total .da file sections\n", 601 total_functions, n_sections); 602 } 603 604 #endif /* DBUS_HAVE_GCC33_GCOV */ 605 606 typedef struct Arc Arc; 607 typedef struct Block Block; 608 typedef struct Function Function; 609 typedef struct File File; 610 typedef struct Line Line; 611 612 struct Arc 613 { 614 int source; 615 int target; 616 dbus_int64_t arc_count; 617 unsigned int count_valid : 1; 618 unsigned int on_tree : 1; 619 unsigned int fake : 1; 620 unsigned int fall_through : 1; 621 Arc *pred_next; 622 Arc *succ_next; 623 }; 624 625 struct Block 626 { 627 Arc *succ; 628 Arc *pred; 629 dbus_int64_t succ_count; 630 dbus_int64_t pred_count; 631 dbus_int64_t exec_count; 632 DBusList *lines; 633 unsigned int count_valid : 1; 634 unsigned int on_tree : 1; 635 unsigned int inside_dbus_build_tests : 1; 636 }; 637 638 struct Function 639 { 640 char *name; 641 int checksum; 642 Block *block_graph; 643 int n_blocks; 644 /* number of blocks in DBUS_BUILD_TESTS */ 645 int n_test_blocks; 646 int n_test_blocks_executed; 647 /* number of blocks outside DBUS_BUILD_TESTS */ 648 int n_nontest_blocks; 649 int n_nontest_blocks_executed; 650 /* Summary result flags */ 651 unsigned int unused : 1; 652 unsigned int inside_dbus_build_tests : 1; 653 unsigned int partial : 1; /* only some of the blocks were executed */ 654 }; 655 656 struct Line 657 { 658 int number; 659 char *text; 660 DBusList *blocks; 661 unsigned int inside_dbus_build_tests : 1; 662 unsigned int partial : 1; /* only some of the blocks were executed */ 663 }; 664 665 struct File 666 { 667 char *name; 668 Line *lines; 669 int n_lines; 670 DBusList *functions; 671 }; 672 673 static void 674 function_add_arc (Function *function, 675 long source, 676 long target, 677 long flags) 678 { 679 Arc *arc; 680 681 arc = dbus_new0 (Arc, 1); 682 if (arc == NULL) 683 die ("no memory\n"); 684 685 arc->target = target; 686 arc->source = source; 687 688 arc->succ_next = function->block_graph[source].succ; 689 function->block_graph[source].succ = arc; 690 function->block_graph[source].succ_count += 1; 691 692 arc->pred_next = function->block_graph[target].pred; 693 function->block_graph[target].pred = arc; 694 function->block_graph[target].pred_count += 1; 695 696 if ((flags & FLAG_ON_TREE) != 0) 697 arc->on_tree = TRUE; 698 699 if ((flags & FLAG_FAKE) != 0) 700 arc->fake = TRUE; 701 702 if ((flags & FLAG_FALL_THROUGH) != 0) 703 arc->fall_through = TRUE; 704 } 705 706 707 static Arc* 708 reverse_arcs (Arc *arc) 709 { 710 struct Arc *prev = 0; 711 struct Arc *next; 712 713 for ( ; arc; arc = next) 714 { 715 next = arc->succ_next; 716 arc->succ_next = prev; 717 prev = arc; 718 } 719 720 return prev; 721 } 722 723 static void 724 function_reverse_succ_arcs (Function *func) 725 { 726 /* Must reverse the order of all succ arcs, to ensure that they match 727 * the order of the data in the .da file. 728 */ 729 int i; 730 731 for (i = 0; i < func->n_blocks; i++) 732 if (func->block_graph[i].succ) 733 func->block_graph[i].succ = reverse_arcs (func->block_graph[i].succ); 734 } 735 736 static void 737 get_functions_from_bbg (const DBusString *contents, 738 DBusList **functions) 739 { 740 int i; 741 long val; 742 int n_functions; 743 int n_arcs; 744 int n_blocks; 745 int n_arcs_off_tree; 746 747 #if 0 748 printf ("Loading arcs and blocks from .bbg file\n"); 749 #endif 750 751 n_arcs_off_tree = 0; 752 n_blocks = 0; 753 n_arcs = 0; 754 n_functions = 0; 755 i = 0; 756 while (i < _dbus_string_get_length (contents)) 757 { 758 Function *func; 759 long n_blocks_in_func; 760 long n_arcs_in_func; 761 int j; 762 763 #ifdef DBUS_HAVE_GCC33_GCOV 764 DBusString funcname; 765 int checksum; 766 767 /* In gcc33 .bbg files, there's a function name of the form: 768 * -1, length, name (padded to 4), -1, checksum 769 * after that header on each function description, it's 770 * the same as in gcc32 771 */ 772 if (!_dbus_string_init (&funcname)) 773 die ("no memory\n"); 774 775 if (!string_get_function (contents, i, 776 &funcname, &checksum, &i)) 777 die ("could not read function name\n"); 778 #endif /* DBUS_HAVE_GCC33_GCOV */ 779 780 if (!string_get_int (contents, i, &val)) 781 break; 782 783 n_blocks_in_func = val; 784 785 i += 4; 786 787 if (!string_get_int (contents, i, &n_arcs_in_func)) 788 break; 789 790 i += 4; 791 792 n_functions += 1; 793 n_blocks += n_blocks_in_func; 794 n_arcs += n_arcs_in_func; 795 796 func = dbus_new0 (Function, 1); 797 if (func == NULL) 798 die ("no memory\n"); 799 800 #ifdef DBUS_HAVE_GCC33_GCOV 801 func->name = _dbus_strdup (_dbus_string_get_const_data (&funcname)); 802 func->checksum = checksum; 803 _dbus_string_free (&funcname); 804 #endif 805 806 func->block_graph = dbus_new0 (Block, n_blocks_in_func); 807 func->n_blocks = n_blocks_in_func; 808 809 j = 0; 810 while (j < n_blocks_in_func) 811 { 812 long n_arcs_in_block; 813 int k; 814 815 if (!string_get_int (contents, i, &n_arcs_in_block)) 816 break; 817 818 i += 4; 819 820 k = 0; 821 while (k < n_arcs_in_block) 822 { 823 long destination_block; 824 long flags; 825 826 if (!string_get_int (contents, i, &destination_block)) 827 break; 828 829 i += 4; 830 831 if (!string_get_int (contents, i, &flags)) 832 break; 833 834 i += 4; 835 836 if ((flags & FLAG_ON_TREE) == 0) 837 n_arcs_off_tree += 1; 838 839 function_add_arc (func, j, destination_block, 840 flags); 841 842 ++k; 843 } 844 845 if (k < n_arcs_in_block) 846 break; 847 848 ++j; 849 } 850 851 if (j < n_blocks_in_func) 852 break; 853 854 function_reverse_succ_arcs (func); 855 856 if (!_dbus_list_append (functions, func)) 857 die ("no memory\n"); 858 859 if (!string_get_int (contents, i, &val)) 860 break; 861 862 i += 4; 863 864 if (val != -1) 865 die ("-1 separator not found in .bbg file\n"); 866 } 867 868 #if 0 869 printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n", 870 n_functions, n_blocks, n_arcs, n_arcs_off_tree); 871 #endif 872 873 _dbus_assert (n_functions == _dbus_list_get_length (functions)); 874 } 875 876 #ifdef DBUS_HAVE_GCC33_GCOV 877 static void 878 add_counts_from_da (const DBusString *contents, 879 DBusList **functions) 880 { 881 int i; 882 dbus_int64_t v64; 883 long val; 884 int n_sections; 885 DBusList *link; 886 Function *current_func; 887 int current_block; 888 Arc *current_arc; 889 890 n_sections = 0; 891 892 i = 0; 893 while (i < _dbus_string_get_length (contents)) 894 { 895 int claimed_n_functions; 896 int n_functions; 897 898 if (!string_get_int (contents, i, &val)) 899 die ("no magic found in .da file\n"); 900 901 i += 4; 902 903 if (val != -123) 904 die ("wrong file magic in .da file\n"); 905 906 if (!string_get_int (contents, i, &val)) 907 die ("no function count in .da file\n"); 908 i += 4; 909 claimed_n_functions = val; 910 911 if (!string_get_int (contents, i, &val)) 912 die ("no extra data length in .da file\n"); 913 914 i += 4; 915 916 i += val; 917 918 link = _dbus_list_get_first_link (functions); 919 if (link == NULL) 920 goto no_more_functions; 921 922 n_functions = 0; 923 while (n_functions < claimed_n_functions && link != NULL) 924 { 925 DBusString funcname; 926 int checksum; 927 int claimed_n_arcs; 928 int n_arcs; 929 930 current_func = link->data; 931 current_block = 0; 932 current_arc = current_func->block_graph[current_block].succ; 933 934 if (!_dbus_string_init (&funcname)) 935 die ("no memory\n"); 936 937 if (!string_get_function (contents, i, 938 &funcname, &checksum, &i)) 939 die ("could not read function name\n"); 940 941 if (!_dbus_string_equal_c_str (&funcname, current_func->name)) 942 { 943 fprintf (stderr, "Expecting .da info for %s but got %s\n", 944 current_func->name, 945 _dbus_string_get_const_data (&funcname)); 946 exit (1); 947 } 948 949 if (checksum != current_func->checksum) 950 die (".da file checksum doesn't match checksum from .bbg file\n"); 951 952 if (!string_get_int (contents, i, &val)) 953 die ("no arc count for function\n"); 954 955 i += 4; 956 claimed_n_arcs = val; 957 958 /* For each arc in the profile, find the corresponding 959 * arc in the function and increment its count 960 */ 961 n_arcs = 0; 962 while (n_arcs < claimed_n_arcs) 963 { 964 if (!string_get_int64 (contents, i, &v64)) 965 die ("did not get execution count for arc\n"); 966 967 i += 8; 968 969 /* Find the next arc in the function that isn't on tree */ 970 while (current_arc == NULL || 971 current_arc->on_tree) 972 { 973 if (current_arc == NULL) 974 { 975 ++current_block; 976 977 if (current_block >= current_func->n_blocks) 978 die ("too many blocks in function\n"); 979 980 current_arc = current_func->block_graph[current_block].succ; 981 } 982 else 983 { 984 current_arc = current_arc->succ_next; 985 } 986 } 987 988 _dbus_assert (current_arc != NULL); 989 _dbus_assert (!current_arc->on_tree); 990 991 current_arc->arc_count = v64; 992 current_arc->count_valid = TRUE; 993 current_func->block_graph[current_block].succ_count -= 1; 994 current_func->block_graph[current_arc->target].pred_count -= 1; 995 996 ++n_arcs; 997 998 current_arc = current_arc->succ_next; 999 } 1000 1001 _dbus_string_free (&funcname); 1002 1003 link = _dbus_list_get_next_link (functions, link); 1004 ++n_functions; 1005 1006 if (link == NULL && n_functions < claimed_n_functions) 1007 { 1008 fprintf (stderr, "Ran out of functions loading .da file\n"); 1009 goto no_more_functions; 1010 } 1011 } 1012 1013 no_more_functions: 1014 1015 ++n_sections; 1016 } 1017 } 1018 #else /* DBUS_HAVE_GCC33_GCOV */ 1019 static void 1020 add_counts_from_da (const DBusString *contents, 1021 DBusList **functions) 1022 { 1023 int i; 1024 dbus_int64_t val; 1025 int n_arcs; 1026 int claimed_n_arcs; 1027 DBusList *link; 1028 Function *current_func; 1029 int current_block; 1030 Arc *current_arc; 1031 1032 #if 0 1033 printf ("Loading execution count for each arc from .da file\n"); 1034 #endif 1035 1036 i = 0; 1037 if (!string_get_int64 (contents, i, &val)) 1038 return; 1039 1040 i += 8; 1041 1042 claimed_n_arcs = val; 1043 1044 link = _dbus_list_get_first_link (functions); 1045 if (link == NULL) 1046 goto done; 1047 1048 current_func = link->data; 1049 current_block = 0; 1050 current_arc = current_func->block_graph[current_block].succ; 1051 1052 n_arcs = 0; 1053 while (string_get_int64 (contents, i, &val)) 1054 { 1055 i += 8; 1056 1057 while (current_arc == NULL || 1058 current_arc->on_tree) 1059 { 1060 if (current_arc == NULL) 1061 { 1062 ++current_block; 1063 1064 if (current_block == current_func->n_blocks) 1065 { 1066 link = _dbus_list_get_next_link (functions, link); 1067 if (link == NULL) 1068 { 1069 fprintf (stderr, "Ran out of functions loading .da file\n"); 1070 goto done; 1071 } 1072 current_func = link->data; 1073 current_block = 0; 1074 } 1075 1076 current_arc = current_func->block_graph[current_block].succ; 1077 } 1078 else 1079 { 1080 current_arc = current_arc->succ_next; 1081 } 1082 } 1083 1084 _dbus_assert (current_arc != NULL); 1085 _dbus_assert (!current_arc->on_tree); 1086 1087 current_arc->arc_count = val; 1088 current_arc->count_valid = TRUE; 1089 current_func->block_graph[current_block].succ_count -= 1; 1090 current_func->block_graph[current_arc->target].pred_count -= 1; 1091 1092 ++n_arcs; 1093 1094 current_arc = current_arc->succ_next; 1095 } 1096 1097 done: 1098 1099 if (n_arcs != claimed_n_arcs) 1100 { 1101 fprintf (stderr, "File claimed to have %d arcs but only had %d\n", 1102 claimed_n_arcs, n_arcs); 1103 exit (1); 1104 } 1105 1106 #if 0 1107 printf ("%d arcs in file\n", n_arcs); 1108 #endif 1109 } 1110 #endif 1111 1112 static void 1113 function_solve_graph (Function *func) 1114 { 1115 int passes, changes; 1116 dbus_int64_t total; 1117 int i; 1118 Arc *arc; 1119 Block *block_graph; 1120 int n_blocks; 1121 1122 #if 0 1123 printf ("Solving function graph\n"); 1124 #endif 1125 1126 n_blocks = func->n_blocks; 1127 block_graph = func->block_graph; 1128 1129 /* For every block in the file, 1130 - if every exit/entrance arc has a known count, then set the block count 1131 - if the block count is known, and every exit/entrance arc but one has 1132 a known execution count, then set the count of the remaining arc 1133 1134 As arc counts are set, decrement the succ/pred count, but don't delete 1135 the arc, that way we can easily tell when all arcs are known, or only 1136 one arc is unknown. */ 1137 1138 /* The order that the basic blocks are iterated through is important. 1139 Since the code that finds spanning trees starts with block 0, low numbered 1140 arcs are put on the spanning tree in preference to high numbered arcs. 1141 Hence, most instrumented arcs are at the end. Graph solving works much 1142 faster if we propagate numbers from the end to the start. 1143 1144 This takes an average of slightly more than 3 passes. */ 1145 1146 changes = 1; 1147 passes = 0; 1148 while (changes) 1149 { 1150 passes++; 1151 changes = 0; 1152 1153 for (i = n_blocks - 1; i >= 0; i--) 1154 { 1155 if (! block_graph[i].count_valid) 1156 { 1157 if (block_graph[i].succ_count == 0) 1158 { 1159 total = 0; 1160 for (arc = block_graph[i].succ; arc; 1161 arc = arc->succ_next) 1162 total += arc->arc_count; 1163 block_graph[i].exec_count = total; 1164 block_graph[i].count_valid = 1; 1165 changes = 1; 1166 } 1167 else if (block_graph[i].pred_count == 0) 1168 { 1169 total = 0; 1170 for (arc = block_graph[i].pred; arc; 1171 arc = arc->pred_next) 1172 total += arc->arc_count; 1173 block_graph[i].exec_count = total; 1174 block_graph[i].count_valid = 1; 1175 changes = 1; 1176 } 1177 } 1178 if (block_graph[i].count_valid) 1179 { 1180 if (block_graph[i].succ_count == 1) 1181 { 1182 total = 0; 1183 /* One of the counts will be invalid, but it is zero, 1184 so adding it in also doesn't hurt. */ 1185 for (arc = block_graph[i].succ; arc; 1186 arc = arc->succ_next) 1187 total += arc->arc_count; 1188 /* Calculate count for remaining arc by conservation. */ 1189 total = block_graph[i].exec_count - total; 1190 /* Search for the invalid arc, and set its count. */ 1191 for (arc = block_graph[i].succ; arc; 1192 arc = arc->succ_next) 1193 if (! arc->count_valid) 1194 break; 1195 if (! arc) 1196 die ("arc == NULL\n"); 1197 arc->count_valid = 1; 1198 arc->arc_count = total; 1199 block_graph[i].succ_count -= 1; 1200 1201 block_graph[arc->target].pred_count -= 1; 1202 changes = 1; 1203 } 1204 if (block_graph[i].pred_count == 1) 1205 { 1206 total = 0; 1207 /* One of the counts will be invalid, but it is zero, 1208 so adding it in also doesn't hurt. */ 1209 for (arc = block_graph[i].pred; arc; 1210 arc = arc->pred_next) 1211 total += arc->arc_count; 1212 /* Calculate count for remaining arc by conservation. */ 1213 total = block_graph[i].exec_count - total; 1214 /* Search for the invalid arc, and set its count. */ 1215 for (arc = block_graph[i].pred; arc; 1216 arc = arc->pred_next) 1217 if (! arc->count_valid) 1218 break; 1219 if (! arc) 1220 die ("arc == NULL\n"); 1221 arc->count_valid = 1; 1222 arc->arc_count = total; 1223 block_graph[i].pred_count -= 1; 1224 1225 block_graph[arc->source].succ_count -= 1; 1226 changes = 1; 1227 } 1228 } 1229 } 1230 } 1231 1232 /* If the graph has been correctly solved, every block will have a 1233 * succ and pred count of zero. 1234 */ 1235 { 1236 dbus_bool_t header = FALSE; 1237 for (i = 0; i < n_blocks; i++) 1238 { 1239 if (block_graph[i].succ_count || block_graph[i].pred_count) 1240 { 1241 if (!header) 1242 { 1243 fprintf (stderr, "WARNING: Block graph solved incorrectly for function %s\n", 1244 func->name); 1245 fprintf (stderr, " this error reflects a bug in decode-gcov.c or perhaps bogus data\n"); 1246 header = TRUE; 1247 } 1248 fprintf (stderr, " block %d has succ_count = %d pred_count = %d\n", 1249 i, (int) block_graph[i].succ_count, (int) block_graph[i].pred_count); 1250 } 1251 } 1252 } 1253 } 1254 1255 static void 1256 solve_graphs (DBusList **functions) 1257 { 1258 DBusList *link; 1259 1260 link = _dbus_list_get_first_link (functions); 1261 while (link != NULL) 1262 { 1263 Function *func = link->data; 1264 1265 function_solve_graph (func); 1266 1267 link = _dbus_list_get_next_link (functions, link); 1268 } 1269 } 1270 1271 static void 1272 load_functions_for_c_file (const DBusString *filename, 1273 DBusList **functions) 1274 { 1275 DBusString bbg_filename; 1276 DBusString da_filename; 1277 DBusString gcno_filename; 1278 DBusString gcda_filename; 1279 DBusString contents; 1280 DBusString *name; 1281 DBusError error; 1282 1283 /* With latest gcc it's .gcno instead of .bbg and 1284 * gcda instead of .da 1285 */ 1286 1287 dbus_error_init (&error); 1288 1289 if (!_dbus_string_init (&bbg_filename) || 1290 !_dbus_string_init (&da_filename) || 1291 !_dbus_string_init (&gcno_filename) || 1292 !_dbus_string_init (&gcda_filename) || 1293 !_dbus_string_copy (filename, 0, &bbg_filename, 0) || 1294 !_dbus_string_copy (filename, 0, &da_filename, 0) || 1295 !_dbus_string_copy (filename, 0, &gcno_filename, 0) || 1296 !_dbus_string_copy (filename, 0, &gcda_filename, 0) || 1297 !_dbus_string_init (&contents)) 1298 die ("no memory\n"); 1299 1300 _dbus_string_shorten (&bbg_filename, 2); 1301 _dbus_string_shorten (&da_filename, 2); 1302 1303 if (!_dbus_string_append (&bbg_filename, ".bbg") || 1304 !_dbus_string_append (&da_filename, ".da") || 1305 !_dbus_string_append (&bbg_filename, ".gcno") || 1306 !_dbus_string_append (&bbg_filename, ".gcda")) 1307 die ("no memory\n"); 1308 1309 if (_dbus_file_exists (_dbus_string_get_const_data (&gcno_filename))) 1310 name = &gcno_filename; 1311 else 1312 name = &bbg_filename; 1313 1314 if (!_dbus_file_get_contents (&contents, name, 1315 &error)) 1316 { 1317 fprintf (stderr, "Could not open file: %s\n", 1318 error.message); 1319 exit (1); 1320 } 1321 1322 get_functions_from_bbg (&contents, functions); 1323 1324 _dbus_string_set_length (&contents, 0); 1325 1326 if (_dbus_file_exists (_dbus_string_get_const_data (&gcda_filename))) 1327 name = &gcda_filename; 1328 else 1329 name = &da_filename; 1330 1331 if (!_dbus_file_get_contents (&contents, name, 1332 &error)) 1333 { 1334 /* Try .libs/file.da */ 1335 int slash; 1336 1337 if (_dbus_string_find_byte_backward (name, 1338 _dbus_string_get_length (name), 1339 '/', 1340 &slash)) 1341 { 1342 DBusString libs; 1343 _dbus_string_init_const (&libs, "/.libs"); 1344 1345 if (!_dbus_string_copy (&libs, 0, name, slash)) 1346 die ("no memory"); 1347 1348 dbus_error_free (&error); 1349 if (!_dbus_file_get_contents (&contents, name, 1350 &error)) 1351 { 1352 fprintf (stderr, "Could not open file: %s\n", 1353 error.message); 1354 exit (1); 1355 } 1356 } 1357 else 1358 { 1359 fprintf (stderr, "Could not open file: %s\n", 1360 error.message); 1361 exit (1); 1362 } 1363 } 1364 1365 add_counts_from_da (&contents, functions); 1366 1367 solve_graphs (functions); 1368 1369 _dbus_string_free (&contents); 1370 _dbus_string_free (&da_filename); 1371 _dbus_string_free (&bbg_filename); 1372 } 1373 1374 static void 1375 get_lines_from_bb_file (const DBusString *contents, 1376 File *fl) 1377 { 1378 int i; 1379 long val; 1380 int n_functions; 1381 dbus_bool_t in_our_file; 1382 DBusList *link; 1383 Function *func; 1384 int block; 1385 1386 #if 0 1387 printf ("Getting line numbers for blocks from .bb file\n"); 1388 #endif 1389 1390 /* There's this "filename" field in the .bb file which 1391 * mysteriously comes *after* the first function in the 1392 * file in the .bb file; and every .bb file seems to 1393 * have only one filename. I don't understand 1394 * what's going on here, so just set in_our_file = TRUE 1395 * at the start categorically. 1396 */ 1397 1398 block = 0; 1399 func = NULL; 1400 in_our_file = TRUE; 1401 link = _dbus_list_get_first_link (&fl->functions); 1402 n_functions = 0; 1403 i = 0; 1404 while (string_get_int (contents, i, &val)) 1405 { 1406 i += 4; 1407 1408 switch (val) 1409 { 1410 case BB_FILENAME: 1411 { 1412 DBusString f; 1413 1414 if (!_dbus_string_init (&f)) 1415 die ("no memory\n"); 1416 1417 if (string_get_string (contents, i, 1418 BB_FILENAME, 1419 &f, &i)) 1420 { 1421 /* fl->name is a full path and the filename in .bb is 1422 * not. 1423 */ 1424 DBusString tmp_str; 1425 1426 _dbus_string_init_const (&tmp_str, fl->name); 1427 1428 if (_dbus_string_ends_with_c_str (&tmp_str, 1429 _dbus_string_get_const_data (&f))) 1430 in_our_file = TRUE; 1431 else 1432 in_our_file = FALSE; 1433 1434 #if 0 1435 fprintf (stderr, 1436 "File %s in .bb, looking for %s, in_our_file = %d\n", 1437 _dbus_string_get_const_data (&f), 1438 fl->name, 1439 in_our_file); 1440 #endif 1441 } 1442 _dbus_string_free (&f); 1443 } 1444 break; 1445 case BB_FUNCTION: 1446 { 1447 DBusString f; 1448 if (!_dbus_string_init (&f)) 1449 die ("no memory\n"); 1450 1451 if (string_get_string (contents, i, 1452 BB_FUNCTION, 1453 &f, &i)) 1454 { 1455 #if 0 1456 fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f)); 1457 #endif 1458 1459 block = 0; 1460 1461 if (in_our_file) 1462 { 1463 if (link == NULL) 1464 { 1465 fprintf (stderr, "No function object for function %s\n", 1466 _dbus_string_get_const_data (&f)); 1467 } 1468 else 1469 { 1470 func = link->data; 1471 link = _dbus_list_get_next_link (&fl->functions, link); 1472 1473 if (func->name == NULL) 1474 { 1475 if (!_dbus_string_copy_data (&f, &func->name)) 1476 die ("no memory\n"); 1477 } 1478 else 1479 { 1480 if (!_dbus_string_equal_c_str (&f, func->name)) 1481 { 1482 fprintf (stderr, "got function name \"%s\" (%d) from .bbg file, but \"%s\" (%d) from .bb file\n", 1483 func->name, strlen (func->name), 1484 _dbus_string_get_const_data (&f), 1485 _dbus_string_get_length (&f)); 1486 1487 } 1488 } 1489 } 1490 } 1491 } 1492 _dbus_string_free (&f); 1493 1494 n_functions += 1; 1495 } 1496 break; 1497 case BB_ENDOFLIST: 1498 block += 1; 1499 break; 1500 default: 1501 #if 0 1502 fprintf (stderr, "Line %ld\n", val); 1503 #endif 1504 1505 if (val >= fl->n_lines) 1506 { 1507 fprintf (stderr, "Line %ld but file only has %d lines\n", 1508 val, fl->n_lines); 1509 } 1510 else if (func != NULL) 1511 { 1512 val -= 1; /* To convert the 1-based line number to 0-based */ 1513 _dbus_assert (val >= 0); 1514 1515 if (block < func->n_blocks) 1516 { 1517 if (!_dbus_list_append (&func->block_graph[block].lines, 1518 &fl->lines[val])) 1519 die ("no memory\n"); 1520 1521 1522 if (!_dbus_list_append (&fl->lines[val].blocks, 1523 &func->block_graph[block])) 1524 die ("no memory\n"); 1525 } 1526 else 1527 { 1528 fprintf (stderr, "Line number for block %d but function only has %d blocks\n", 1529 block, func->n_blocks); 1530 } 1531 } 1532 else 1533 { 1534 fprintf (stderr, "Line %ld given outside of any function\n", 1535 val); 1536 } 1537 1538 break; 1539 } 1540 } 1541 1542 #if 0 1543 printf ("%d functions in file\n", n_functions); 1544 #endif 1545 } 1546 1547 1548 static void 1549 load_block_line_associations (const DBusString *filename, 1550 File *f) 1551 { 1552 DBusString bb_filename; 1553 DBusString contents; 1554 DBusError error; 1555 1556 dbus_error_init (&error); 1557 1558 if (!_dbus_string_init (&bb_filename) || 1559 !_dbus_string_copy (filename, 0, &bb_filename, 0) || 1560 !_dbus_string_init (&contents)) 1561 die ("no memory\n"); 1562 1563 _dbus_string_shorten (&bb_filename, 2); 1564 1565 if (!_dbus_string_append (&bb_filename, ".bb")) 1566 die ("no memory\n"); 1567 1568 if (!_dbus_file_get_contents (&contents, &bb_filename, 1569 &error)) 1570 { 1571 fprintf (stderr, "Could not open file: %s\n", 1572 error.message); 1573 exit (1); 1574 } 1575 1576 get_lines_from_bb_file (&contents, f); 1577 1578 _dbus_string_free (&contents); 1579 _dbus_string_free (&bb_filename); 1580 } 1581 1582 static int 1583 count_lines_in_string (const DBusString *str) 1584 { 1585 int n_lines; 1586 const char *p; 1587 const char *prev; 1588 const char *end; 1589 const char *last_line_end; 1590 1591 #if 0 1592 printf ("Counting lines in source file\n"); 1593 #endif 1594 1595 n_lines = 0; 1596 prev = NULL; 1597 p = _dbus_string_get_const_data (str); 1598 end = p + _dbus_string_get_length (str); 1599 last_line_end = p; 1600 while (p != end) 1601 { 1602 /* too lazy to handle \r\n as one linebreak */ 1603 if (*p == '\n' || *p == '\r') 1604 { 1605 ++n_lines; 1606 last_line_end = p + 1; 1607 } 1608 1609 prev = p; 1610 ++p; 1611 } 1612 1613 if (last_line_end != p) 1614 ++n_lines; 1615 1616 return n_lines; 1617 } 1618 1619 static void 1620 fill_line_content (const DBusString *str, 1621 Line *lines) 1622 { 1623 int n_lines; 1624 const char *p; 1625 const char *prev; 1626 const char *end; 1627 const char *last_line_end; 1628 1629 #if 0 1630 printf ("Saving contents of each line in source file\n"); 1631 #endif 1632 1633 n_lines = 0; 1634 prev = NULL; 1635 p = _dbus_string_get_const_data (str); 1636 end = p + _dbus_string_get_length (str); 1637 last_line_end = p; 1638 while (p != end) 1639 { 1640 if (*p == '\n' || *p == '\r') 1641 { 1642 lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1); 1643 if (lines[n_lines].text == NULL) 1644 die ("no memory\n"); 1645 1646 memcpy (lines[n_lines].text, last_line_end, p - last_line_end); 1647 lines[n_lines].number = n_lines + 1; 1648 1649 ++n_lines; 1650 1651 last_line_end = p + 1; 1652 } 1653 1654 prev = p; 1655 ++p; 1656 } 1657 1658 if (p != last_line_end) 1659 { 1660 memcpy (lines[n_lines].text, last_line_end, p - last_line_end); 1661 ++n_lines; 1662 } 1663 } 1664 1665 static void 1666 mark_inside_dbus_build_tests (File *f) 1667 { 1668 int i; 1669 DBusList *link; 1670 int inside_depth; 1671 1672 inside_depth = 0; 1673 i = 0; 1674 while (i < f->n_lines) 1675 { 1676 Line *l = &f->lines[i]; 1677 dbus_bool_t is_verbose; 1678 1679 is_verbose = strstr (l->text, "_dbus_verbose") != NULL; 1680 1681 if (inside_depth == 0) 1682 { 1683 const char *a, *b; 1684 1685 a = strstr (l->text, "#if"); 1686 b = strstr (l->text, "DBUS_BUILD_TESTS"); 1687 if (a && b && (a < b)) 1688 inside_depth += 1; 1689 } 1690 else 1691 { 1692 if (strstr (l->text, "#if") != NULL) 1693 inside_depth += 1; 1694 else if (strstr (l->text, "#endif") != NULL) 1695 inside_depth -= 1; 1696 } 1697 1698 if (inside_depth > 0 || is_verbose) 1699 { 1700 /* Mark the line and its blocks */ 1701 DBusList *blink; 1702 1703 l->inside_dbus_build_tests = TRUE; 1704 1705 blink = _dbus_list_get_first_link (&l->blocks); 1706 while (blink != NULL) 1707 { 1708 Block *b = blink->data; 1709 1710 b->inside_dbus_build_tests = TRUE; 1711 1712 blink = _dbus_list_get_next_link (&l->blocks, blink); 1713 } 1714 } 1715 1716 ++i; 1717 } 1718 1719 /* Now mark functions where for all blocks that are associated 1720 * with a source line, the block is inside_dbus_build_tests. 1721 */ 1722 link = _dbus_list_get_first_link (&f->functions); 1723 while (link != NULL) 1724 { 1725 Function *func = link->data; 1726 1727 /* The issue is that some blocks aren't associated with a source line. 1728 * Assume they are inside/outside tests according to the source 1729 * line of the preceding block. For the first block, make it 1730 * match the first following block with a line associated. 1731 */ 1732 if (func->block_graph[0].lines == NULL) 1733 { 1734 /* find first following line */ 1735 i = 1; 1736 while (i < func->n_blocks) 1737 { 1738 if (func->block_graph[i].lines != NULL) 1739 { 1740 func->block_graph[0].inside_dbus_build_tests = 1741 func->block_graph[i].inside_dbus_build_tests; 1742 break; 1743 } 1744 1745 ++i; 1746 } 1747 } 1748 1749 /* Now mark all blocks but the first */ 1750 i = 1; 1751 while (i < func->n_blocks) 1752 { 1753 if (func->block_graph[i].lines == NULL) 1754 { 1755 func->block_graph[i].inside_dbus_build_tests = 1756 func->block_graph[i-1].inside_dbus_build_tests; 1757 } 1758 1759 ++i; 1760 } 1761 1762 i = 0; 1763 while (i < func->n_blocks) 1764 { 1765 /* Break as soon as any block is not a test block */ 1766 if (func->block_graph[i].lines != NULL && 1767 !func->block_graph[i].inside_dbus_build_tests) 1768 break; 1769 1770 ++i; 1771 } 1772 1773 if (i == func->n_blocks) 1774 func->inside_dbus_build_tests = TRUE; 1775 1776 link = _dbus_list_get_next_link (&f->functions, link); 1777 } 1778 } 1779 1780 static void 1781 mark_coverage (File *f) 1782 { 1783 int i; 1784 DBusList *link; 1785 1786 i = 0; 1787 while (i < f->n_lines) 1788 { 1789 Line *l = &f->lines[i]; 1790 DBusList *blink; 1791 int n_blocks; 1792 int n_blocks_executed; 1793 1794 n_blocks = 0; 1795 n_blocks_executed = 0; 1796 blink = _dbus_list_get_first_link (&l->blocks); 1797 while (blink != NULL) 1798 { 1799 Block *b = blink->data; 1800 1801 if (b->exec_count > 0) 1802 n_blocks_executed += 1; 1803 1804 n_blocks += 1; 1805 1806 blink = _dbus_list_get_next_link (&l->blocks, blink); 1807 } 1808 1809 if (n_blocks_executed > 0 && 1810 n_blocks_executed < n_blocks) 1811 l->partial = TRUE; 1812 1813 ++i; 1814 } 1815 1816 link = _dbus_list_get_first_link (&f->functions); 1817 while (link != NULL) 1818 { 1819 Function *func = link->data; 1820 int i; 1821 int n_test_blocks; 1822 int n_test_blocks_executed; 1823 int n_nontest_blocks; 1824 int n_nontest_blocks_executed; 1825 1826 n_test_blocks = 0; 1827 n_test_blocks_executed = 0; 1828 n_nontest_blocks = 0; 1829 n_nontest_blocks_executed = 0; 1830 1831 i = 0; 1832 while (i < func->n_blocks) 1833 { 1834 if (!func->block_graph[i].inside_dbus_build_tests) 1835 { 1836 n_nontest_blocks += 1; 1837 1838 if (func->block_graph[i].exec_count > 0) 1839 n_nontest_blocks_executed += 1; 1840 } 1841 else 1842 { 1843 n_test_blocks += 1; 1844 1845 if (func->block_graph[i].exec_count > 0) 1846 n_test_blocks_executed += 1; 1847 } 1848 1849 ++i; 1850 } 1851 1852 if (n_nontest_blocks_executed > 0 && 1853 n_nontest_blocks_executed < n_nontest_blocks) 1854 func->partial = TRUE; 1855 1856 if (n_nontest_blocks_executed == 0 && 1857 n_nontest_blocks > 0) 1858 func->unused = TRUE; 1859 1860 func->n_test_blocks = n_test_blocks; 1861 func->n_test_blocks_executed = n_test_blocks_executed; 1862 func->n_nontest_blocks = n_nontest_blocks; 1863 func->n_nontest_blocks_executed = n_nontest_blocks_executed; 1864 1865 link = _dbus_list_get_next_link (&f->functions, link); 1866 } 1867 } 1868 1869 static File* 1870 load_c_file (const DBusString *filename) 1871 { 1872 DBusString contents; 1873 DBusError error; 1874 File *f; 1875 1876 f = dbus_new0 (File, 1); 1877 if (f == NULL) 1878 die ("no memory\n"); 1879 1880 if (!_dbus_string_copy_data (filename, &f->name)) 1881 die ("no memory\n"); 1882 1883 if (!_dbus_string_init (&contents)) 1884 die ("no memory\n"); 1885 1886 dbus_error_init (&error); 1887 1888 if (!_dbus_file_get_contents (&contents, filename, 1889 &error)) 1890 { 1891 fprintf (stderr, "Could not open file: %s\n", 1892 error.message); 1893 dbus_error_free (&error); 1894 exit (1); 1895 } 1896 1897 load_functions_for_c_file (filename, &f->functions); 1898 1899 f->n_lines = count_lines_in_string (&contents); 1900 f->lines = dbus_new0 (Line, f->n_lines); 1901 if (f->lines == NULL) 1902 die ("no memory\n"); 1903 1904 fill_line_content (&contents, f->lines); 1905 1906 _dbus_string_free (&contents); 1907 1908 load_block_line_associations (filename, f); 1909 1910 mark_inside_dbus_build_tests (f); 1911 mark_coverage (f); 1912 1913 return f; 1914 } 1915 1916 typedef struct Stats Stats; 1917 1918 struct Stats 1919 { 1920 int n_blocks; 1921 int n_blocks_executed; 1922 int n_blocks_inside_dbus_build_tests; 1923 1924 int n_lines; /* lines that have blocks on them */ 1925 int n_lines_executed; 1926 int n_lines_partial; 1927 int n_lines_inside_dbus_build_tests; 1928 1929 int n_functions; 1930 int n_functions_executed; 1931 int n_functions_partial; 1932 int n_functions_inside_dbus_build_tests; 1933 }; 1934 1935 static dbus_bool_t 1936 line_was_executed (Line *l) 1937 { 1938 DBusList *link; 1939 1940 link = _dbus_list_get_first_link (&l->blocks); 1941 while (link != NULL) 1942 { 1943 Block *b = link->data; 1944 1945 if (b->exec_count > 0) 1946 return TRUE; 1947 1948 link = _dbus_list_get_next_link (&l->blocks, link); 1949 } 1950 1951 return FALSE; 1952 } 1953 1954 1955 static int 1956 line_exec_count (Line *l) 1957 { 1958 DBusList *link; 1959 dbus_int64_t total; 1960 1961 total = 0; 1962 link = _dbus_list_get_first_link (&l->blocks); 1963 while (link != NULL) 1964 { 1965 Block *b = link->data; 1966 1967 total += b->exec_count; 1968 1969 link = _dbus_list_get_next_link (&l->blocks, link); 1970 } 1971 1972 return total; 1973 } 1974 1975 static void 1976 merge_stats_for_file (Stats *stats, 1977 File *f) 1978 { 1979 int i; 1980 DBusList *link; 1981 1982 for (i = 0; i < f->n_lines; ++i) 1983 { 1984 Line *l = &f->lines[i]; 1985 1986 if (l->inside_dbus_build_tests) 1987 { 1988 stats->n_lines_inside_dbus_build_tests += 1; 1989 continue; 1990 } 1991 1992 if (line_was_executed (l)) 1993 stats->n_lines_executed += 1; 1994 1995 if (l->blocks != NULL) 1996 stats->n_lines += 1; 1997 1998 if (l->partial) 1999 stats->n_lines_partial += 1; 2000 } 2001 2002 link = _dbus_list_get_first_link (&f->functions); 2003 while (link != NULL) 2004 { 2005 Function *func = link->data; 2006 2007 if (func->inside_dbus_build_tests) 2008 stats->n_functions_inside_dbus_build_tests += 1; 2009 else 2010 { 2011 stats->n_functions += 1; 2012 2013 if (!func->unused) 2014 stats->n_functions_executed += 1; 2015 2016 if (func->partial) 2017 stats->n_functions_partial += 1; 2018 } 2019 2020 stats->n_blocks_inside_dbus_build_tests += 2021 func->n_test_blocks; 2022 2023 stats->n_blocks_executed += 2024 func->n_nontest_blocks_executed; 2025 2026 stats->n_blocks += 2027 func->n_nontest_blocks; 2028 2029 link = _dbus_list_get_next_link (&f->functions, link); 2030 } 2031 } 2032 2033 /* The output of this matches gcov exactly ("diff" shows no difference) */ 2034 static void 2035 print_annotated_source_gcov_format (File *f) 2036 { 2037 int i; 2038 2039 i = 0; 2040 while (i < f->n_lines) 2041 { 2042 Line *l = &f->lines[i]; 2043 2044 if (l->blocks != NULL) 2045 { 2046 int exec_count; 2047 2048 exec_count = line_exec_count (l); 2049 2050 if (exec_count > 0) 2051 printf ("%12d %s\n", 2052 exec_count, l->text); 2053 else 2054 printf (" ###### %s\n", l->text); 2055 } 2056 else 2057 { 2058 printf ("\t\t%s\n", l->text); 2059 } 2060 2061 ++i; 2062 } 2063 } 2064 2065 static void 2066 print_annotated_source (File *f) 2067 { 2068 int i; 2069 2070 i = 0; 2071 while (i < f->n_lines) 2072 { 2073 Line *l = &f->lines[i]; 2074 2075 if (l->inside_dbus_build_tests) 2076 printf ("*"); 2077 else 2078 printf (" "); 2079 2080 if (l->blocks != NULL) 2081 { 2082 int exec_count; 2083 2084 exec_count = line_exec_count (l); 2085 2086 if (exec_count > 0) 2087 printf ("%12d %s\n", 2088 exec_count, l->text); 2089 else 2090 printf (" ###### %s\n", l->text); 2091 } 2092 else 2093 { 2094 printf ("\t\t%s\n", l->text); 2095 } 2096 2097 ++i; 2098 } 2099 } 2100 2101 static void 2102 print_block_superdetails (File *f) 2103 { 2104 DBusList *link; 2105 int i; 2106 2107 link = _dbus_list_get_first_link (&f->functions); 2108 while (link != NULL) 2109 { 2110 Function *func = link->data; 2111 2112 printf ("=== %s():\n", func->name); 2113 2114 i = 0; 2115 while (i < func->n_blocks) 2116 { 2117 Block *b = &func->block_graph[i]; 2118 DBusList *l; 2119 2120 printf (" %5d executed %d times%s\n", i, 2121 (int) b->exec_count, 2122 b->inside_dbus_build_tests ? 2123 " [inside DBUS_BUILD_TESTS]" : ""); 2124 2125 l = _dbus_list_get_first_link (&b->lines); 2126 while (l != NULL) 2127 { 2128 Line *line = l->data; 2129 2130 printf ("4%d\t%s\n", line->number, line->text); 2131 2132 l = _dbus_list_get_next_link (&b->lines, l); 2133 } 2134 2135 ++i; 2136 } 2137 2138 link = _dbus_list_get_next_link (&f->functions, link); 2139 } 2140 } 2141 2142 static void 2143 print_one_file (const DBusString *filename) 2144 { 2145 if (_dbus_string_ends_with_c_str (filename, ".bb")) 2146 { 2147 DBusString contents; 2148 DBusError error; 2149 2150 if (!_dbus_string_init (&contents)) 2151 die ("no memory\n"); 2152 2153 dbus_error_init (&error); 2154 2155 if (!_dbus_file_get_contents (&contents, filename, 2156 &error)) 2157 { 2158 fprintf (stderr, "Could not open file: %s\n", 2159 error.message); 2160 dbus_error_free (&error); 2161 exit (1); 2162 } 2163 2164 dump_bb_file (&contents); 2165 2166 _dbus_string_free (&contents); 2167 } 2168 else if (_dbus_string_ends_with_c_str (filename, ".bbg")) 2169 { 2170 DBusString contents; 2171 DBusError error; 2172 2173 if (!_dbus_string_init (&contents)) 2174 die ("no memory\n"); 2175 2176 dbus_error_init (&error); 2177 2178 if (!_dbus_file_get_contents (&contents, filename, 2179 &error)) 2180 { 2181 fprintf (stderr, "Could not open file: %s\n", 2182 error.message); 2183 dbus_error_free (&error); 2184 exit (1); 2185 } 2186 2187 dump_bbg_file (&contents); 2188 2189 _dbus_string_free (&contents); 2190 } 2191 else if (_dbus_string_ends_with_c_str (filename, ".da")) 2192 { 2193 DBusString contents; 2194 DBusError error; 2195 2196 if (!_dbus_string_init (&contents)) 2197 die ("no memory\n"); 2198 2199 dbus_error_init (&error); 2200 2201 if (!_dbus_file_get_contents (&contents, filename, 2202 &error)) 2203 { 2204 fprintf (stderr, "Could not open file: %s\n", 2205 error.message); 2206 dbus_error_free (&error); 2207 exit (1); 2208 } 2209 2210 dump_da_file (&contents); 2211 2212 _dbus_string_free (&contents); 2213 } 2214 else if (_dbus_string_ends_with_c_str (filename, ".c")) 2215 { 2216 File *f; 2217 2218 f = load_c_file (filename); 2219 2220 print_annotated_source (f); 2221 } 2222 else 2223 { 2224 fprintf (stderr, "Unknown file type %s\n", 2225 _dbus_string_get_const_data (filename)); 2226 exit (1); 2227 } 2228 } 2229 2230 static void 2231 print_untested_functions (File *f) 2232 { 2233 DBusList *link; 2234 dbus_bool_t found; 2235 2236 found = FALSE; 2237 link = _dbus_list_get_first_link (&f->functions); 2238 while (link != NULL) 2239 { 2240 Function *func = link->data; 2241 2242 if (func->unused && 2243 !func->inside_dbus_build_tests) 2244 found = TRUE; 2245 2246 link = _dbus_list_get_next_link (&f->functions, link); 2247 } 2248 2249 if (!found) 2250 return; 2251 2252 printf ("Untested functions in %s\n", f->name); 2253 printf ("=======\n"); 2254 2255 link = _dbus_list_get_first_link (&f->functions); 2256 while (link != NULL) 2257 { 2258 Function *func = link->data; 2259 2260 if (func->unused && 2261 !func->inside_dbus_build_tests) 2262 printf (" %s\n", func->name); 2263 2264 link = _dbus_list_get_next_link (&f->functions, link); 2265 } 2266 2267 printf ("\n"); 2268 } 2269 2270 static void 2271 print_poorly_tested_functions (File *f, 2272 Stats *stats) 2273 { 2274 DBusList *link; 2275 dbus_bool_t found; 2276 2277 #define TEST_FRACTION(function) ((function)->n_nontest_blocks_executed / (double) (function)->n_nontest_blocks) 2278 2279 #define AVERAGE_COVERAGE ((stats)->n_blocks_executed / (double) (stats)->n_blocks) 2280 2281 #define POORLY_TESTED(function) (!(function)->unused && \ 2282 (function)->n_nontest_blocks > 0 && \ 2283 TEST_FRACTION (function) < AVERAGE_COVERAGE) 2284 2285 found = FALSE; 2286 link = _dbus_list_get_first_link (&f->functions); 2287 while (link != NULL) 2288 { 2289 Function *func = link->data; 2290 2291 if (POORLY_TESTED (func)) 2292 found = TRUE; 2293 2294 link = _dbus_list_get_next_link (&f->functions, link); 2295 } 2296 2297 if (!found) 2298 return; 2299 2300 printf ("Below average functions in %s\n", f->name); 2301 printf ("=======\n"); 2302 2303 link = _dbus_list_get_first_link (&f->functions); 2304 while (link != NULL) 2305 { 2306 Function *func = link->data; 2307 2308 if (POORLY_TESTED (func)) 2309 printf (" %s (%d%%)\n", func->name, 2310 (int) (TEST_FRACTION (func) * 100)); 2311 2312 link = _dbus_list_get_next_link (&f->functions, link); 2313 } 2314 2315 printf ("\n"); 2316 } 2317 2318 static int 2319 func_cmp (const void *a, 2320 const void *b) 2321 { 2322 Function *af = *(Function**) a; 2323 Function *bf = *(Function**) b; 2324 int a_untested = af->n_nontest_blocks - af->n_nontest_blocks_executed; 2325 int b_untested = bf->n_nontest_blocks - bf->n_nontest_blocks_executed; 2326 2327 /* Sort by number of untested blocks */ 2328 return b_untested - a_untested; 2329 } 2330 2331 static void 2332 print_n_untested_blocks_by_function (File *f, 2333 Stats *stats) 2334 { 2335 DBusList *link; 2336 Function **funcs; 2337 int n_found; 2338 int i; 2339 2340 n_found = 0; 2341 link = _dbus_list_get_first_link (&f->functions); 2342 while (link != NULL) 2343 { 2344 Function *func = link->data; 2345 2346 if (func->n_nontest_blocks_executed < 2347 func->n_nontest_blocks) 2348 n_found += 1; 2349 2350 link = _dbus_list_get_next_link (&f->functions, link); 2351 } 2352 2353 if (n_found == 0) 2354 return; 2355 2356 /* make an array so we can use qsort */ 2357 2358 funcs = dbus_new (Function*, n_found); 2359 if (funcs == NULL) 2360 return; 2361 2362 i = 0; 2363 link = _dbus_list_get_first_link (&f->functions); 2364 while (link != NULL) 2365 { 2366 Function *func = link->data; 2367 2368 if (func->n_nontest_blocks_executed < 2369 func->n_nontest_blocks) 2370 { 2371 funcs[i] = func; 2372 ++i; 2373 } 2374 2375 link = _dbus_list_get_next_link (&f->functions, link); 2376 } 2377 2378 _dbus_assert (i == n_found); 2379 2380 qsort (funcs, n_found, sizeof (Function*), 2381 func_cmp); 2382 2383 printf ("Incomplete functions in %s\n", f->name); 2384 printf ("=======\n"); 2385 2386 i = 0; 2387 while (i < n_found) 2388 { 2389 Function *func = funcs[i]; 2390 2391 printf (" %s (%d/%d untested blocks)\n", 2392 func->name, 2393 func->n_nontest_blocks - func->n_nontest_blocks_executed, 2394 func->n_nontest_blocks); 2395 2396 ++i; 2397 } 2398 2399 dbus_free (funcs); 2400 2401 printf ("\n"); 2402 } 2403 2404 static void 2405 print_stats (Stats *stats, 2406 const char *of_what) 2407 { 2408 int completely; 2409 2410 printf ("Summary (%s)\n", of_what); 2411 printf ("=======\n"); 2412 printf (" %g%% blocks executed (%d of %d)\n", 2413 (stats->n_blocks_executed / (double) stats->n_blocks) * 100.0, 2414 stats->n_blocks_executed, 2415 stats->n_blocks); 2416 2417 printf (" (ignored %d blocks of test-only/debug-only code)\n", 2418 stats->n_blocks_inside_dbus_build_tests); 2419 2420 printf (" %g%% functions executed (%d of %d)\n", 2421 (stats->n_functions_executed / (double) stats->n_functions) * 100.0, 2422 stats->n_functions_executed, 2423 stats->n_functions); 2424 2425 completely = stats->n_functions_executed - stats->n_functions_partial; 2426 printf (" %g%% functions completely executed (%d of %d)\n", 2427 (completely / (double) stats->n_functions) * 100.0, 2428 completely, 2429 stats->n_functions); 2430 2431 printf (" (ignored %d functions of test-only/debug-only code)\n", 2432 stats->n_functions_inside_dbus_build_tests); 2433 2434 printf (" %g%% lines executed (%d of %d)\n", 2435 (stats->n_lines_executed / (double) stats->n_lines) * 100.0, 2436 stats->n_lines_executed, 2437 stats->n_lines); 2438 2439 completely = stats->n_lines_executed - stats->n_lines_partial; 2440 printf (" %g%% lines completely executed (%d of %d)\n", 2441 (completely / (double) stats->n_lines) * 100.0, 2442 completely, 2443 stats->n_lines); 2444 2445 printf (" (ignored %d lines of test-only/debug-only code)\n", 2446 stats->n_lines_inside_dbus_build_tests); 2447 2448 printf ("\n"); 2449 } 2450 2451 typedef enum 2452 { 2453 MODE_PRINT, 2454 MODE_REPORT, 2455 MODE_BLOCKS, 2456 MODE_GCOV 2457 } Mode; 2458 2459 int 2460 main (int argc, char **argv) 2461 { 2462 DBusString filename; 2463 int i; 2464 Mode m; 2465 2466 if (argc < 2) 2467 { 2468 fprintf (stderr, "Must specify files on command line\n"); 2469 return 1; 2470 } 2471 2472 m = MODE_PRINT; 2473 i = 1; 2474 2475 if (strcmp (argv[i], "--report") == 0) 2476 { 2477 m = MODE_REPORT; 2478 ++i; 2479 } 2480 else if (strcmp (argv[i], "--blocks") == 0) 2481 { 2482 m = MODE_BLOCKS; 2483 ++i; 2484 } 2485 else if (strcmp (argv[i], "--gcov") == 0) 2486 { 2487 m = MODE_GCOV; 2488 ++i; 2489 } 2490 2491 2492 if (i == argc) 2493 { 2494 fprintf (stderr, "Must specify files on command line\n"); 2495 return 1; 2496 } 2497 2498 if (m == MODE_PRINT) 2499 { 2500 while (i < argc) 2501 { 2502 _dbus_string_init_const (&filename, argv[i]); 2503 2504 print_one_file (&filename); 2505 2506 ++i; 2507 } 2508 } 2509 else if (m == MODE_BLOCKS || m == MODE_GCOV) 2510 { 2511 while (i < argc) 2512 { 2513 File *f; 2514 2515 _dbus_string_init_const (&filename, argv[i]); 2516 2517 f = load_c_file (&filename); 2518 2519 if (m == MODE_BLOCKS) 2520 print_block_superdetails (f); 2521 else if (m == MODE_GCOV) 2522 print_annotated_source_gcov_format (f); 2523 2524 ++i; 2525 } 2526 } 2527 else if (m == MODE_REPORT) 2528 { 2529 Stats stats = { 0, }; 2530 DBusList *files; 2531 DBusList *link; 2532 DBusHashTable *stats_by_dir; 2533 DBusHashIter iter; 2534 2535 files = NULL; 2536 while (i < argc) 2537 { 2538 _dbus_string_init_const (&filename, argv[i]); 2539 2540 if (_dbus_string_ends_with_c_str (&filename, ".c")) 2541 { 2542 File *f; 2543 2544 f = load_c_file (&filename); 2545 2546 if (!_dbus_list_append (&files, f)) 2547 die ("no memory\n"); 2548 } 2549 else 2550 { 2551 fprintf (stderr, "Unknown file type %s\n", 2552 _dbus_string_get_const_data (&filename)); 2553 exit (1); 2554 } 2555 2556 ++i; 2557 } 2558 2559 link = _dbus_list_get_first_link (&files); 2560 while (link != NULL) 2561 { 2562 File *f = link->data; 2563 2564 merge_stats_for_file (&stats, f); 2565 2566 link = _dbus_list_get_next_link (&files, link); 2567 } 2568 2569 print_stats (&stats, "all files"); 2570 2571 stats_by_dir = _dbus_hash_table_new (DBUS_HASH_STRING, 2572 dbus_free, dbus_free); 2573 2574 link = _dbus_list_get_first_link (&files); 2575 while (link != NULL) 2576 { 2577 File *f = link->data; 2578 DBusString dirname; 2579 char *dirname_c; 2580 Stats *dir_stats; 2581 2582 _dbus_string_init_const (&filename, f->name); 2583 2584 if (!_dbus_string_init (&dirname)) 2585 die ("no memory\n"); 2586 2587 if (!_dbus_string_get_dirname (&filename, &dirname) || 2588 !_dbus_string_copy_data (&dirname, &dirname_c)) 2589 die ("no memory\n"); 2590 2591 dir_stats = _dbus_hash_table_lookup_string (stats_by_dir, 2592 dirname_c); 2593 2594 if (dir_stats == NULL) 2595 { 2596 dir_stats = dbus_new0 (Stats, 1); 2597 if (!_dbus_hash_table_insert_string (stats_by_dir, dirname_c, 2598 dir_stats)) 2599 die ("no memory\n"); 2600 } 2601 else 2602 dbus_free (dirname_c); 2603 2604 merge_stats_for_file (dir_stats, f); 2605 2606 link = _dbus_list_get_next_link (&files, link); 2607 } 2608 2609 _dbus_hash_iter_init (stats_by_dir, &iter); 2610 while (_dbus_hash_iter_next (&iter)) 2611 { 2612 const char *dirname = _dbus_hash_iter_get_string_key (&iter); 2613 Stats *dir_stats = _dbus_hash_iter_get_value (&iter); 2614 2615 print_stats (dir_stats, dirname); 2616 } 2617 2618 _dbus_hash_table_unref (stats_by_dir); 2619 2620 link = _dbus_list_get_first_link (&files); 2621 while (link != NULL) 2622 { 2623 File *f = link->data; 2624 2625 print_untested_functions (f); 2626 2627 link = _dbus_list_get_next_link (&files, link); 2628 } 2629 2630 link = _dbus_list_get_first_link (&files); 2631 while (link != NULL) 2632 { 2633 File *f = link->data; 2634 2635 print_poorly_tested_functions (f, &stats); 2636 2637 link = _dbus_list_get_next_link (&files, link); 2638 } 2639 2640 link = _dbus_list_get_first_link (&files); 2641 while (link != NULL) 2642 { 2643 File *f = link->data; 2644 2645 print_n_untested_blocks_by_function (f, &stats); 2646 2647 link = _dbus_list_get_next_link (&files, link); 2648 } 2649 } 2650 2651 return 0; 2652 } 2653