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