1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509
|
# Copyright (C) 2013-2015 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Notes:
# 1) This follows a Python convention for marking internal vs public functions.
# Internal functions are prefixed with "_".
# A simple testcase generator.
#
# Usage Notes:
#
# 1) The length of each parameter list must either be one, in which case the
# same value is used for each run, or the length must match all other
# parameters of length greater than one.
#
# 2) Values for parameters that vary across runs must appear in increasing
# order. E.g. nr_gen_shlibs = { 0 1 10 } is good, { 1 0 10 } is bad.
# This rule simplifies the code a bit, without being onerous on the user:
# a) Report generation doesn't have to sort the output by run, it'll already
# be sorted.
# b) In the static object file case, the last run can be used used to generate
# all the source files.
#
# TODO:
# 1) have functions call each other within an objfile and across
# objfiles to measure things like backtrace times
# 2) enums
#
# Implementation Notes:
#
# 1) The implementation would be a bit simpler if we could assume Tcl 8.5.
# Then we could use a dictionary to record the testcase instead of an array.
# With the array we use here, there is only one copy of it and instead of
# passing its value we pass its name. Yay Tcl. An alternative is to just
# use a global variable.
#
# 2) Because these programs can be rather large, we try to avoid recompilation
# where we can. We don't have a makefile: we could generate one but it's
# not clear that's simpler than our chosen mechanism which is to record
# sums of all the inputs, and detect if an input has changed that way.
if ![info exists CAT_PROGRAM] {
set CAT_PROGRAM "/bin/cat"
}
# TODO(dje): Time md5sum vs sha1sum with our testcases.
if ![info exists SHA1SUM_PROGRAM] {
set SHA1SUM_PROGRAM "/usr/bin/sha1sum"
}
namespace eval GenPerfTest {
# The default level of compilation parallelism we support.
set DEFAULT_PERF_TEST_COMPILE_PARALLELISM 10
# The language of the test.
set DEFAULT_LANGUAGE "c"
# Extra source files for the binary.
# This must at least include the file with main(),
# and each test must supply its own.
set DEFAULT_BINARY_EXTRA_SOURCES {}
# Header files used by generated files and extra sources.
set DEFAULT_BINARY_EXTRA_HEADERS {}
# Extra source files for each generated shlib.
# The compiler passes -DSHLIB=NNN which the source can use, for example,
# to define unique symbols for each shlib.
set DEFAULT_GEN_SHLIB_EXTRA_SOURCES {}
# Header files used by generated files and extra sources.
set DEFAULT_GEN_SHLIB_EXTRA_HEADERS {}
# Source files for a tail shlib, or empty if none.
# This library is loaded after all other shlibs (except any system shlibs
# like libstdc++). It is useful for exercising issues that can appear
# with system shlibs, without having to cope with implementation details
# and bugs in system shlibs. E.g., gcc pr 65669.
set DEFAULT_TAIL_SHLIB_SOURCES {}
# Header files for the tail shlib.
set DEFAULT_TAIL_SHLIB_HEADERS {}
# The number of shared libraries to create.
set DEFAULT_NR_GEN_SHLIBS 0
# The number of compunits in each objfile.
set DEFAULT_NR_COMPUNITS 1
# The number of public globals in each compunit.
set DEFAULT_NR_EXTERN_GLOBALS 1
# The number of static globals in each compunit.
set DEFAULT_NR_STATIC_GLOBALS 1
# The number of public functions in each compunit.
set DEFAULT_NR_EXTERN_FUNCTIONS 1
# The number of static functions in each compunit.
set DEFAULT_NR_STATIC_FUNCTIONS 1
# Class generation.
# This is only used if the selected language permits it.
# The class specs here are used for each compunit.
# Additional flexibility can be added as needed, but for now KISS.
#
# key/value list of:
# count: number of classes
# Default: 1
# name: list of namespaces and class name prefix
# E.g., { ns0 ns1 foo } -> ns0::ns1::foo_<cu#>_{0,1,...}
# There is no default, this value must be specified.
# nr_members: number of members
# Default: 0
# nr_static_members: number of static members
# Default: 0
# nr_methods: number of methods
# Default: 0
# nr_inline_methods: number of inline methods
# Default: 0
# nr_static_methods: number of static methods
# Default: 0
# nr_static_inline_methods: number of static inline methods
# Default: 0
#
# E.g.,
# class foo {};
# namespace ns1 { class foo {}; }
# namespace ns2 { class bar {}; }
# would be represented as
# {
# { count 1 name { foo } }
# { count 1 name { ns1 foo } }
# { count 1 name { ns2 bar } }
# }
# The actual generated class names will be
# cu_N_foo_0, ns1::cu_N_foo_0, ns2::cu_N_bar_0
# where "N" is the CU number.
#
# To keep things simple for now, all class definitions go in headers,
# one class per header, with non-inline method definitions going
# into corresponding source files.
set DEFAULT_CLASS_SPECS {}
# The default value for the "count" field of class_specs.
set DEFAULT_CLASS_COUNT 1
# The default number of members in each class.
set DEFAULT_CLASS_NR_MEMBERS 0
# The default number of static members in each class.
set DEFAULT_CLASS_NR_STATIC_MEMBERS 0
# The default number of methods in each class.
set DEFAULT_CLASS_NR_METHODS 0
# The default number of inline methods in each class.
set DEFAULT_CLASS_NR_INLINE_METHODS 0
# The default number of static methods in each class.
set DEFAULT_CLASS_NR_STATIC_METHODS 0
# The default number of static inline methods in each class.
set DEFAULT_CLASS_NR_STATIC_INLINE_METHODS 0
set header_suffixes(c) "h"
set header_suffixes(c++) "h"
set source_suffixes(c) "c"
set source_suffixes(c++) "cc"
# Generate .worker files that control building all the "pieces" of the
# testcase. This doesn't include "main" or any test-specific stuff.
# This mostly consists of the "bulk" (aka "crap" :-)) of the testcase to
# give gdb something meaty to chew on.
# The result is 0 for success, -1 for failure.
#
# Benchmarks generated by some of the tests are big. I mean really big.
# And it's a pain to build one piece at a time, we need a parallel build.
# To achieve this, given the framework we're working with, we need to
# generate arguments to pass to a parallel make. This is done by
# generating several files and then passing the file names to the parallel
# make. All of the needed info is contained in the file name, so we could
# do this differently, but this is pretty simple and flexible.
proc gen_worker_files { test_description_exp } {
global objdir PERF_TEST_COMPILE_PARALLELISM
if { [file tail $test_description_exp] != $test_description_exp } {
error "test description file contains directory name"
}
set program_name [file rootname $test_description_exp]
set workers_dir "$objdir/gdb.perf/workers/$program_name"
file mkdir $workers_dir
set nr_workers $PERF_TEST_COMPILE_PARALLELISM
verbose -log "gen_worker_files: $test_description_exp $nr_workers workers"
for { set i 0 } { $i < $nr_workers } { incr i } {
set file_name "${workers_dir}/${program_name}-${i}.worker"
verbose -log "gen_worker_files: Generating $file_name"
set f [open $file_name "w"]
puts $f "# DO NOT EDIT, machine generated file."
puts $f "# See perftest.exp:GenPerfTest::gen_worker_files."
close $f
}
return 0
}
# Load a perftest description.
# Test descriptions are used to build the input files (binary + shlibs)
# of one or more performance tests.
proc load_test_description { basename } {
global srcdir
if { [file tail $basename] != $basename } {
error "test description file contains directory name"
}
verbose -log "load_file $srcdir/gdb.perf/$basename"
if { [load_file $srcdir/gdb.perf/$basename] == 0 } {
error "Unable to load test description $basename"
}
}
# Create a testcase object for test NAME.
# The caller must call this as:
# array set my_test [GenPerfTest::init_testcase $name]
proc init_testcase { name } {
set testcase(name) $name
set testcase(language) $GenPerfTest::DEFAULT_LANGUAGE
set testcase(run_names) [list $name]
set testcase(binary_extra_sources) $GenPerfTest::DEFAULT_BINARY_EXTRA_SOURCES
set testcase(binary_extra_headers) $GenPerfTest::DEFAULT_BINARY_EXTRA_HEADERS
set testcase(gen_shlib_extra_sources) $GenPerfTest::DEFAULT_GEN_SHLIB_EXTRA_SOURCES
set testcase(gen_shlib_extra_headers) $GenPerfTest::DEFAULT_GEN_SHLIB_EXTRA_HEADERS
set testcase(tail_shlib_sources) $GenPerfTest::DEFAULT_TAIL_SHLIB_SOURCES
set testcase(tail_shlib_headers) $GenPerfTest::DEFAULT_TAIL_SHLIB_HEADERS
set testcase(nr_gen_shlibs) $GenPerfTest::DEFAULT_NR_GEN_SHLIBS
set testcase(nr_compunits) $GenPerfTest::DEFAULT_NR_COMPUNITS
set testcase(nr_extern_globals) $GenPerfTest::DEFAULT_NR_EXTERN_GLOBALS
set testcase(nr_static_globals) $GenPerfTest::DEFAULT_NR_STATIC_GLOBALS
set testcase(nr_extern_functions) $GenPerfTest::DEFAULT_NR_EXTERN_FUNCTIONS
set testcase(nr_static_functions) $GenPerfTest::DEFAULT_NR_STATIC_FUNCTIONS
set testcase(class_specs) $GenPerfTest::DEFAULT_CLASS_SPECS
# The location of this file drives the location of all other files.
# The choice is derived from standard_output_file. We don't use it
# because of the parallel build support, we want each worker's log/sum
# files to go in different directories, but we don't want their output
# to go in different directories.
# N.B. The value here must be kept in sync with Makefile.in.
global objdir
set name_no_spaces [_convert_spaces $name]
set testcase(binfile) "$objdir/gdb.perf/outputs/$name_no_spaces/$name_no_spaces"
return [array get testcase]
}
proc _verify_parameter_lengths { self_var } {
upvar 1 $self_var self
set params {
binary_extra_sources binary_extra_headers
gen_shlib_extra_sources gen_shlib_extra_headers
tail_shlib_sources tail_shlib_headers
nr_gen_shlibs nr_compunits
nr_extern_globals nr_static_globals
nr_extern_functions nr_static_functions
class_specs
}
set nr_runs [llength $self(run_names)]
foreach p $params {
set n [llength $self($p)]
if { $n > 1 } {
if { $n != $nr_runs } {
error "Bad number of values for parameter $p"
}
set values $self($p)
for { set i 0 } { $i < $n - 1 } { incr i } {
if { [lindex $values $i] > [lindex $values [expr $i + 1]] } {
error "Values of parameter $p are not increasing"
}
}
}
}
}
# Verify the class_specs parameter.
proc _verify_class_specs { self_var } {
upvar 1 $self_var self
set nr_runs [llength $self(run_names)]
for { set run_nr 0 } { $run_nr < $nr_runs } { incr run_nr } {
set class_specs [_get_param $self(class_specs) $run_nr]
foreach { spec } $class_specs {
if { [llength $spec] % 2 != 0 } {
error "Uneven number of values in class spec: $spec"
}
foreach { key value } $spec {
switch -exact -- $key {
name { }
count -
nr_members - nr_static_members -
nr_methods - nr_static_methods -
nr_inline_methods - nr_static_inline_methods
{
if ![string is integer $value] {
error "Non-integer value $value for key $key in class_specs: $class_specs"
}
}
default {
error "Unrecognized key $key in class_specs: $class_specs"
}
}
}
}
}
}
# Verify the testcase is valid (as best we can, this isn't exhaustive).
proc _verify_testcase { self_var } {
upvar 1 $self_var self
_verify_parameter_lengths self
_verify_class_specs self
# Each test must supply its own main(). We don't check for main here,
# but we do verify the test supplied something.
if { [llength $self(binary_extra_sources)] == 0 } {
error "Missing value for binary_extra_sources"
}
}
# Return the value of parameter PARAM for run RUN_NR.
proc _get_param { param run_nr } {
if { [llength $param] == 1 } {
# Since PARAM may be a list of lists we need to use lindex. This
# also works for scalars (scalars are degenerate lists).
return [lindex $param 0]
}
return [lindex $param $run_nr]
}
# Return non-zero if all files (binaries + shlibs) can be compiled from
# one set of object files. This is a simple optimization to speed up
# test build times. This happens if the only variation among runs is
# nr_gen_shlibs or nr_compunits.
proc _static_object_files_p { self_var } {
upvar 1 $self_var self
# These values are either scalars, or can vary across runs but don't
# affect whether we can share the generated object objects between
# runs.
set static_object_file_params {
name language run_names nr_gen_shlibs nr_compunits
binary_extra_sources gen_shlib_extra_sources tail_shlib_sources
}
foreach name [array names self] {
if { [lsearch $static_object_file_params $name] < 0 } {
# name is not in static_object_file_params.
if { [llength $self($name)] > 1 } {
# The user could provide a list that is all the same value,
# so check for that.
set first_value [lindex $self($name) 0]
foreach elm [lrange $self($name) 1 end] {
if { $elm != $first_value } {
return 0
}
}
}
}
}
return 1
}
# Return non-zero if classes are enabled.
proc _classes_enabled_p { self_var run_nr } {
upvar 1 $self_var self
set class_specs [_get_param $self(class_specs) $run_nr]
return [expr [llength $class_specs] > 0]
}
# Spaces in file names are a pain, remove them.
# They appear if the user puts spaces in the test name or run name.
proc _convert_spaces { file_name } {
return [regsub -all " " $file_name "-"]
}
# Return the compilation flags for the test.
proc _compile_options { self_var } {
upvar 1 $self_var self
set result {debug}
switch $self(language) {
c++ {
lappend result "c++"
}
}
return $result
}
# Return the path to put source/object files in for run number RUN_NR.
proc _make_object_dir_name { self_var static run_nr } {
upvar 1 $self_var self
# Note: The output directory already includes the name of the test
# description file.
set bindir [file dirname $self(binfile)]
# Put the pieces in a subdirectory, there are a lot of them.
if $static {
return "$bindir/pieces"
} else {
set run_name [_convert_spaces [lindex $self(run_names) $run_nr]]
return "$bindir/pieces/$run_name"
}
}
# RUN_NR is ignored if STATIC is non-zero.
# SO_NR is the shlib number or "" for the binary.
# CU_NR is either the compilation unit number or "main".
proc _make_header_basename { self_var static run_nr so_nr cu_nr } {
upvar 1 $self_var self
set header_suffix $GenPerfTest::header_suffixes($self(language))
if { !$static } {
set run_name [_get_param $self(run_names) $run_nr]
if { "$so_nr" != "" } {
set header_name "${run_name}-lib${so_nr}-cu${cu_nr}.$header_suffix"
} else {
set header_name "${run_name}-cu${cu_nr}.$header_suffix"
}
} else {
if { "$so_nr" != "" } {
set header_name "lib${so_nr}-cu${cu_nr}.$header_suffix"
} else {
set header_name "cu${cu_nr}.$header_suffix"
}
}
return "[_convert_spaces $header_name]"
}
# RUN_NR is ignored if STATIC is non-zero.
# SO_NR is the shlib number or "" for the binary.
# CU_NR is either the compilation unit number or "main".
proc _make_header_name { self_var static run_nr so_nr cu_nr } {
upvar 1 $self_var self
set header_name [_make_header_basename self $static $run_nr $so_nr $cu_nr]
return "[_make_object_dir_name self $static $run_nr]/$header_name"
}
# RUN_NR is ignored if STATIC is non-zero.
# SO_NR is the shlib number or "" for the binary.
# CU_NR is either the compilation unit number or "main".
proc _make_source_basename { self_var static run_nr so_nr cu_nr } {
upvar 1 $self_var self
set source_suffix $GenPerfTest::source_suffixes($self(language))
if { !$static } {
set run_name [_get_param $self(run_names) $run_nr]
if { "$so_nr" != "" } {
set source_name "${run_name}-lib${so_nr}-cu${cu_nr}.$source_suffix"
} else {
set source_name "${run_name}-cu${cu_nr}.$source_suffix"
}
} else {
if { "$so_nr" != "" } {
set source_name "lib${so_nr}-cu${cu_nr}.$source_suffix"
} else {
set source_name "cu${cu_nr}.$source_suffix"
}
}
return "[_convert_spaces $source_name]"
}
# RUN_NR is ignored if STATIC is non-zero.
# SO_NR is the shlib number or "" for the binary.
# CU_NR is either the compilation unit number or "main".
proc _make_source_name { self_var static run_nr so_nr cu_nr } {
upvar 1 $self_var self
set source_name [_make_source_basename self $static $run_nr $so_nr $cu_nr]
return "[_make_object_dir_name self $static $run_nr]/$source_name"
}
# Generated object files get put in the same directory as their source.
# WARNING: This means that we can't do parallel compiles from the same
# source file, they have to have different names.
proc _make_binary_object_name { self_var static run_nr cu_nr } {
upvar 1 $self_var self
set source_name [_make_source_name self $static $run_nr "" $cu_nr]
return [file rootname $source_name].o
}
# Return the list of source/object files for the binary.
# This is the source files specified in test param binary_extra_sources as
# well as the names of all the object file "pieces".
# STATIC is the value of _static_object_files_p for the test.
proc _make_binary_input_file_names { self_var static run_nr } {
upvar 1 $self_var self
global srcdir subdir
set nr_compunits [_get_param $self(nr_compunits) $run_nr]
set result {}
foreach source [_get_param $self(binary_extra_sources) $run_nr] {
lappend result "$srcdir/$subdir/$source"
}
for { set cu_nr 0 } { $cu_nr < $nr_compunits } { incr cu_nr } {
lappend result [_make_binary_object_name self $static $run_nr $cu_nr]
}
return $result
}
proc _make_binary_name { self_var run_nr } {
upvar 1 $self_var self
set run_name [_get_param $self(run_names) $run_nr]
set exe_name "$self(binfile)-[_convert_spaces ${run_name}]"
return $exe_name
}
# SO_NAME is either a shlib number or "tail".
proc _make_shlib_name { self_var static run_nr so_name } {
upvar 1 $self_var self
if { !$static } {
set run_name [_get_param $self(run_names) $run_nr]
set lib_name "$self(name)-${run_name}-lib${so_name}.so"
} else {
set lib_name "$self(name)-lib${so_name}.so"
}
set output_dir [file dirname $self(binfile)]
return "[_make_object_dir_name self $static $run_nr]/[_convert_spaces $lib_name]"
}
proc _create_file { self_var path } {
upvar 1 $self_var self
verbose -log "Creating file: $path"
set f [open $path "w"]
return $f
}
proc _write_intro { self_var f } {
upvar 1 $self_var self
puts $f "// DO NOT EDIT, machine generated file."
puts $f "// See perftest.exp:GenPerfTest."
}
proc _write_includes { self_var f includes } {
upvar 1 $self_var self
if { [llength $includes] > 0 } {
puts $f ""
}
foreach i $includes {
switch -glob -- $i {
"<*>" {
puts $f "#include $i"
}
default {
puts $f "#include \"$i\""
}
}
}
}
proc _make_header_macro { name c } {
return [string toupper "${name}_${c}"]
}
proc _write_static_globals { self_var f run_nr } {
upvar 1 $self_var self
puts $f ""
set nr_static_globals [_get_param $self(nr_static_globals) $run_nr]
# Rather than parameterize the number of const/non-const globals,
# and their types, we keep it simple for now. Even the number of
# bss/non-bss globals may be useful; later, if warranted.
for { set i 0 } { $i < $nr_static_globals } { incr i } {
if { $i % 2 == 0 } {
set const "const "
} else {
set const ""
}
puts $f "static ${const}int static_global_$i = $i;"
}
}
# ID is "" for the binary, and a unique symbol prefix for each SO.
proc _write_extern_globals { self_var f run_nr id cu_nr } {
upvar 1 $self_var self
puts $f ""
set nr_extern_globals [_get_param $self(nr_extern_globals) $run_nr]
# Rather than parameterize the number of const/non-const globals,
# and their types, we keep it simple for now. Even the number of
# bss/non-bss globals may be useful; later, if warranted.
for { set i 0 } { $i < $nr_extern_globals } { incr i } {
if { $i % 2 == 0 } {
set const "const "
} else {
set const ""
}
puts $f "${const}int ${id}global_${cu_nr}_$i = $cu_nr * 1000 + $i;"
}
}
proc _write_static_functions { self_var f run_nr } {
upvar 1 $self_var self
set nr_static_functions [_get_param $self(nr_static_functions) $run_nr]
for { set i 0 } { $i < $nr_static_functions } { incr i } {
puts $f ""
puts $f "static void"
puts $f "static_function_$i (void)"
puts $f "{"
puts $f "}"
}
}
# ID is "" for the binary, and a unique symbol prefix for each SO.
proc _write_extern_functions { self_var f run_nr id cu_nr } {
upvar 1 $self_var self
set nr_extern_functions [_get_param $self(nr_extern_functions) $run_nr]
for { set i 0 } { $i < $nr_extern_functions } { incr i } {
puts $f ""
puts $f "void"
puts $f "${id}function_${cu_nr}_$i (void)"
puts $f "{"
puts $f "}"
}
}
proc _get_class_spec { spec name } {
foreach { key value } $spec {
if { $key == $name } {
return $value
}
}
switch $name {
count {
return $GenPerfTest::DEFAULT_CLASS_COUNT
}
nr_members {
return $GenPerfTest::DEFAULT_CLASS_NR_MEMBERS
}
nr_static_members {
return $GenPerfTest::DEFAULT_CLASS_NR_STATIC_MEMBERS
}
nr_methods {
return $GenPerfTest::DEFAULT_CLASS_NR_METHODS
}
nr_inline_methods {
return $GenPerfTest::DEFAULT_CLASS_NR_INLINE_METHODS
}
nr_static_methods {
return $GenPerfTest::DEFAULT_CLASS_NR_STATIC_METHODS
}
nr_static_inline_methods {
return $GenPerfTest::DEFAULT_CLASS_NR_STATIC_INLINE_METHODS
}
default {
error "required class-spec not present: $name"
}
}
}
# SO_NR is the shlib number or "" for the binary.
# CU_NR is either the compilation unit number or "main".
# NAME is the "name" field from the class spec, which is
# { ns0 ns1 ... nsN class_name }.
# C is the iteration number, from the "count" field from the class spec.
proc _make_class_name { so_nr cu_nr name c } {
set class_name [lindex $name [expr [llength $name] - 1]]
if { "$so_nr" != "" } {
set prefix "shlib${so_nr}_"
} else {
set prefix ""
}
return "${prefix}cu_${cu_nr}_${class_name}_${c}"
}
proc _make_namespace_name { name } {
if { "$name" == "anonymous" } {
return ""
}
return $name
}
proc _write_class_definitions { self_var f static run_nr so_nr cu_nr } {
upvar 1 $self_var self
set class_specs [_get_param $self(class_specs) $run_nr]
foreach spec $class_specs {
set count [_get_class_spec $spec count]
set name [_get_class_spec $spec name]
set nr_members [_get_class_spec $spec nr_members]
set nr_static_members [_get_class_spec $spec nr_static_members]
set nr_methods [_get_class_spec $spec nr_methods]
set nr_static_methods [_get_class_spec $spec nr_static_methods]
set depth [expr [llength $name] - 1]
for { set c 0 } { $c < $count } { incr c } {
puts $f ""
for { set i 0 } { $i < $depth } { incr i } {
puts $f "namespace [_make_namespace_name [lindex $name $i]]"
puts $f "\{"
puts $f ""
}
set class_name [_make_class_name $so_nr $cu_nr $name $c]
puts $f "class $class_name"
puts $f "\{"
puts $f " public:"
for { set i 0 } { $i < $nr_members } { incr i } {
puts $f " int member_$i;"
}
for { set i 0 } { $i < $nr_static_members } { incr i } {
# Rather than parameterize the number of const/non-const
# members, and their types, we keep it simple for now.
if { $i % 2 == 0 } {
puts $f " static const int static_member_$i = $i;"
} else {
puts $f " static int static_member_$i;"
}
}
for { set i 0 } { $i < $nr_methods } { incr i } {
puts $f " void method_$i (void);"
}
for { set i 0 } { $i < $nr_static_methods } { incr i } {
puts $f " static void static_method_$i (void);"
}
_write_inline_methods self $f $so_nr $cu_nr $spec $c
_write_static_inline_methods self $f $so_nr $cu_nr $spec $c
puts $f "\};"
for { set i [expr $depth - 1] } { $i >= 0 } { incr i -1 } {
puts $f ""
puts $f "\} // [lindex $name $i]"
}
}
}
}
proc _write_inline_methods { self_var f so_nr cu_nr spec c } {
upvar 1 $self_var self
set name [_get_class_spec $spec name]
set nr_inline_methods [_get_class_spec $spec nr_inline_methods]
for { set i 0 } { $i < $nr_inline_methods } { incr i } {
puts $f " void inline_method_$i (void) { }"
}
}
proc _write_static_inline_methods { self_var f so_nr cu_nr spec c } {
upvar 1 $self_var self
set name [_get_class_spec $spec name]
set nr_static_inline_methods [_get_class_spec $spec nr_static_inline_methods]
for { set i 0 } { $i < $nr_static_inline_methods } { incr i } {
puts $f " static void static_inline_method_$i (void) { }"
}
}
proc _write_class_implementations { self_var f static run_nr so_nr cu_nr } {
upvar 1 $self_var self
set class_specs [_get_param $self(class_specs) $run_nr]
foreach spec $class_specs {
set count [_get_class_spec $spec count]
set name [_get_class_spec $spec name]
set depth [expr [llength $name] - 1]
for { set c 0 } { $c < $count } { incr c } {
for { set i 0 } { $i < $depth } { incr i } {
puts $f ""
puts $f "namespace [_make_namespace_name [lindex $name $i]]"
puts $f "\{"
}
_write_static_members self $f $so_nr $cu_nr $spec $c
_write_methods self $f $so_nr $cu_nr $spec $c
_write_static_methods self $f $so_nr $cu_nr $spec $c
for { set i [expr $depth - 1] } { $i >= 0 } { incr i -1 } {
puts $f ""
puts $f "\} // [lindex $name $i]"
}
}
}
}
proc _write_static_members { self_var f so_nr cu_nr spec c } {
upvar 1 $self_var self
set name [_get_class_spec $spec name]
set nr_static_members [_get_class_spec $spec nr_static_members]
set class_name [_make_class_name $so_nr $cu_nr $name $c]
puts $f ""
# Rather than parameterize the number of const/non-const
# members, and their types, we keep it simple for now.
for { set i 0 } { $i < $nr_static_members } { incr i } {
if { $i % 2 == 0 } {
# Static const members are initialized inline.
} else {
puts $f "int ${class_name}::static_member_$i = $i;"
}
}
}
proc _write_methods { self_var f so_nr cu_nr spec c } {
upvar 1 $self_var self
set name [_get_class_spec $spec name]
set nr_methods [_get_class_spec $spec nr_methods]
set class_name [_make_class_name $so_nr $cu_nr $name $c]
for { set i 0 } { $i < $nr_methods } { incr i } {
puts $f ""
puts $f "void"
puts $f "${class_name}::method_$i (void)"
puts $f "{"
puts $f "}"
}
}
proc _write_static_methods { self_var f so_nr cu_nr spec c } {
upvar 1 $self_var self
set name [_get_class_spec $spec name]
set nr_static_methods [_get_class_spec $spec nr_static_methods]
set class_name [_make_class_name $so_nr $cu_nr $name $c]
for { set i 0 } { $i < $nr_static_methods } { incr i } {
puts $f ""
puts $f "void"
puts $f "${class_name}::static_method_$i (void)"
puts $f "{"
puts $f "}"
}
}
proc _gen_compunit_header { self_var static run_nr so_nr cu_nr } {
upvar 1 $self_var self
set header_file [_make_header_name self $static $run_nr $so_nr $cu_nr]
set f [_create_file self $header_file]
_write_intro self $f
set header_macro [_make_header_macro "HEADER_CU" $cu_nr]
puts $f ""
puts $f "#ifndef $header_macro"
puts $f "#define $header_macro"
if [_classes_enabled_p self $run_nr] {
_write_class_definitions self $f $static $run_nr $so_nr $cu_nr
}
puts $f ""
puts $f "#endif // $header_macro"
close $f
return $header_file
}
proc _gen_binary_compunit_source { self_var static run_nr cu_nr } {
upvar 1 $self_var self
set source_file [_make_source_name self $static $run_nr "" $cu_nr]
set f [_create_file self $source_file]
_write_intro self $f
_write_includes self $f [_get_param $self(binary_extra_headers) $run_nr]
set header_file [_make_header_basename self $static $run_nr "" $cu_nr]
puts $f "#include \"$header_file\""
_write_static_globals self $f $run_nr
_write_extern_globals self $f $run_nr "" $cu_nr
_write_static_functions self $f $run_nr
_write_extern_functions self $f $run_nr "" $cu_nr
if [_classes_enabled_p self $run_nr] {
_write_class_implementations self $f $static $run_nr "" $cu_nr
}
close $f
return $source_file
}
# Generate the sources for the pieces of the binary.
# The result is a list of source file names and accompanying object file
# names. The pieces are split across workers.
# E.g., with 10 workers the result for worker 0 is
# { { source0 header0 object0 } { source10 header10 object10 } ... }
proc _gen_binary_source { self_var worker_nr static run_nr } {
upvar 1 $self_var self
verbose -log "GenPerfTest::_gen_binary_source worker $worker_nr run $run_nr, started [timestamp -format %c]"
set nr_compunits [_get_param $self(nr_compunits) $run_nr]
global PERF_TEST_COMPILE_PARALLELISM
set nr_workers $PERF_TEST_COMPILE_PARALLELISM
set result {}
for { set cu_nr $worker_nr } { $cu_nr < $nr_compunits } { incr cu_nr $nr_workers } {
set header_file [_gen_compunit_header self $static $run_nr "" $cu_nr]
set source_file [_gen_binary_compunit_source self $static $run_nr $cu_nr]
set object_file [_make_binary_object_name self $static $run_nr $cu_nr]
lappend result [list $source_file $header_file $object_file]
}
verbose -log "GenPerfTest::_gen_binary_source worker $worker_nr run $run_nr, done [timestamp -format %c]"
return $result
}
proc _gen_shlib_compunit_source { self_var static run_nr so_nr cu_nr } {
upvar 1 $self_var self
set source_file [_make_source_name self $static $run_nr $so_nr $cu_nr]
set f [_create_file self $source_file]
_write_intro self $f
_write_includes self $f [_get_param $self(gen_shlib_extra_headers) $run_nr]
set header_file [_make_header_basename self $static $run_nr $so_nr $cu_nr]
puts $f "#include \"$header_file\""
_write_static_globals self $f $run_nr
_write_extern_globals self $f $run_nr "shlib${so_nr}_" $cu_nr
_write_static_functions self $f $run_nr
_write_extern_functions self $f $run_nr "shlib${so_nr}_" $cu_nr
if [_classes_enabled_p self $run_nr] {
_write_class_implementations self $f $static $run_nr $so_nr $cu_nr
}
close $f
return $source_file
}
# CU_NAME is a name from gen_shlib_extra_sources or tail_shlib_sources.
proc _make_shlib_common_source_name { self_var static run_nr so_nr cu_name } {
upvar 1 $self_var self
if { !$static } {
set run_name [_get_param $self(run_names) $run_nr]
set source_name "${run_name}-lib${so_nr}-${cu_name}"
} else {
set source_name "lib${so_nr}-${cu_name}"
}
return "[_make_object_dir_name self $static $run_nr]/[_convert_spaces $source_name]"
}
# N.B. gdb_compile_shlib doesn't support parallel builds of shlibs from
# common sources: the .o file path will be the same across all shlibs.
# gen_shlib_extra_sources may be common across all shlibs but they're each
# compiled with -DSHLIB=$SHLIB so we need different .o files for each
# shlib, and therefore we need different source files for each shlib.
# If this turns out to be too cumbersome we can augment gdb_compile_shlib.
proc _gen_shlib_common_source { self_var static run_nr so_nr source_name } {
upvar 1 $self_var self
global srcdir
set source_file [_make_shlib_common_source_name self $static $run_nr $so_nr $source_name]
file copy -force "$srcdir/gdb.perf/$source_name" ${source_file}
return $source_file
}
# Generate the sources for a shared library.
# The result is a list of source and header file names.
# E.g., { { source0 source1 ... common0 ... } { header0 header1 ... } }
proc _gen_shlib_source { self_var static run_nr so_nr } {
upvar 1 $self_var self
verbose -log "GenPerfTest::_gen_shlib_source run $run_nr so $so_nr, started [timestamp -format %c]"
set headers {}
set sources {}
set nr_compunits [_get_param $self(nr_compunits) $run_nr]
for { set cu_nr 0 } { $cu_nr < $nr_compunits } { incr cu_nr } {
lappend headers [_gen_compunit_header self $static $run_nr $so_nr $cu_nr]
lappend sources [_gen_shlib_compunit_source self $static $run_nr $so_nr $cu_nr]
}
foreach source_name [_get_param $self(gen_shlib_extra_sources) $run_nr] {
lappend sources [_gen_shlib_common_source self $static $run_nr $so_nr $source_name]
}
verbose -log "GenPerfTest::_gen_shlib_source run $run_nr so $so_nr, done [timestamp -format %c]"
return [list $sources $headers]
}
# Write Tcl array ARRAY_NAME to F.
proc _write_tcl_array { self_var f array_name } {
upvar 1 $self_var self
if { "$array_name" != "$self_var" } {
global $array_name
}
puts $f "== $array_name =="
foreach { name value } [array get $array_name] {
puts $f "$name: $value"
}
}
# Write global Tcl state used for compilation to F.
# If anything changes we want to recompile.
proc _write_tcl_state { self_var f dest } {
upvar 1 $self_var self
# TODO(dje): gdb_default_target_compile references a lot of global
# state. Can we capture it all? For now these are the important ones.
set vars { CC_FOR_TARGET CXX_FOR_TARGET CFLAGS_FOR_TARGET }
foreach v $vars {
global $v
if [info exists $v] {
eval set value $$v
puts $f "$v: $value"
}
}
puts $f ""
_write_tcl_array self $f target_info
puts $f ""
_write_tcl_array self $f board_info
}
# Write all sideband non-file inputs, as well as OPTIONS to INPUTS_FILE.
# If anything changes we want to recompile.
proc _write_inputs_file { self_var dest inputs_file options } {
upvar 1 $self_var self
global env
set f [open $inputs_file "w"]
_write_tcl_array self $f self
puts $f ""
puts $f "options: $options"
puts $f "PATH: [getenv PATH]"
puts $f ""
_write_tcl_state self $f $dest
close $f
}
# Generate the sha1sum of all the inputs.
# The result is a list of { error_code text }.
# Upon success error_code is zero and text is the sha1sum.
# Otherwise, error_code is non_zero and text is the error message.
proc _gen_sha1sum_for_inputs { source_files header_files inputs } {
global srcdir subdir CAT_PROGRAM SHA1SUM_PROGRAM
set header_paths ""
foreach f $header_files {
switch -glob -- $f {
"<*>" {
# skip
}
"*gdb.perf/outputs/*" {
# in build tree
append header_paths " $f"
}
default {
append header_paths " $srcdir/$subdir/$f"
}
}
}
verbose -log "_gen_sha1sum_for_inputs: summing $source_files $header_paths $inputs"
set catch_result [catch "exec $CAT_PROGRAM $source_files $header_paths $inputs | $SHA1SUM_PROGRAM" output]
return [list $catch_result $output]
}
# Return the contents of TEXT_FILE.
# It is assumed TEXT_FILE exists and is readable.
# This is used for reading files containing sha1sums, the
# last newline is removed.
proc _read_file { text_file } {
set f [open $text_file "r"]
set result [read -nonewline $f]
close $f
return $result
}
# Write TEXT to TEXT_FILE.
# It is assumed TEXT_FILE can be opened/created and written to.
proc _write_file { text_file text } {
set f [open $text_file "w"]
puts $f $text
close $f
}
# Wrapper on gdb_compile* that computes sha1sums of inputs to decide
# whether the compile is needed.
# The result is the result of gdb_compile*: "" == success, otherwise
# a compilation error occurred and the output is an error message.
# This doesn't take all inputs into account, just the useful ones.
# As an extension (or simplification) on gdb_compile*, if TYPE is
# shlib then call gdb_compile_shlib, otherwise call gdb_compile.
# Other possibilities *could* be handled this way, e.g., pthreads. TBD.
proc _perftest_compile { self_var source_files header_files dest type options } {
upvar 1 $self_var self
verbose -log "_perftest_compile $source_files $header_files $dest $type $options"
# To keep things simple, we put all non-file inputs into a file and
# then cat all input files through sha1sum.
set sha1sum_file ${dest}.sha1sum
set sha1new_file ${dest}.sha1new
set inputs_file ${dest}.inputs
global srcdir subdir
set all_options $options
lappend all_options "incdir=$srcdir/$subdir"
_write_inputs_file self $dest $inputs_file $all_options
set sha1sum [_gen_sha1sum_for_inputs $source_files $header_files $inputs_file]
if { [lindex $sha1sum 0] != 0 } {
return "sha1sum generation error: [lindex $sha1sum 1]"
}
set sha1sum [lindex $sha1sum 1]
if ![file exists $dest] {
file delete $sha1sum_file
}
if [file exists $sha1sum_file] {
set last_sha1sum [_read_file $sha1sum_file]
verbose -log "last: $last_sha1sum, new: $sha1sum"
if { $sha1sum == $last_sha1sum } {
verbose -log "using existing build for $dest"
return ""
}
}
# No such luck, we need to compile.
file delete $sha1sum_file
if { $type == "shlib" } {
set result [gdb_compile_shlib $source_files $dest $all_options]
} else {
set result [gdb_compile $source_files $dest $type $all_options]
}
if { $result == "" } {
_write_file $sha1sum_file $sha1sum
verbose -log "wrote sha1sum: $sha1sum"
}
return $result
}
proc _compile_binary_pieces { self_var worker_nr static run_nr } {
upvar 1 $self_var self
set compile_options [_compile_options self]
set nr_compunits [_get_param $self(nr_compunits) $run_nr]
set extra_headers [_get_param $self(binary_extra_headers) $run_nr]
global PERF_TEST_COMPILE_PARALLELISM
set nr_workers $PERF_TEST_COMPILE_PARALLELISM
# Generate the source first so we can more easily measure how long that
# takes. [It doesn't take hardly any time at all, relative to the time
# it takes to compile it, but this will provide numbers to show that.]
set todo_list [_gen_binary_source self $worker_nr $static $run_nr]
verbose -log "GenPerfTest::_compile_binary_pieces worker $worker_nr run $run_nr, started [timestamp -format %c]"
foreach elm $todo_list {
set source_file [lindex $elm 0]
set header_file [lindex $elm 1]
set object_file [lindex $elm 2]
set all_header_files $extra_headers
lappend all_header_files $header_file
set compile_result [_perftest_compile self $source_file $all_header_files $object_file object $compile_options]
if { $compile_result != "" } {
verbose -log "GenPerfTest::_compile_binary_pieces worker $worker_nr run $run_nr, failed [timestamp -format %c]"
verbose -log $compile_result
return -1
}
}
verbose -log "GenPerfTest::_compile_binary_pieces worker $worker_nr run $run_nr, done [timestamp -format %c]"
return 0
}
# Helper function to compile the pieces of a shlib.
# Note: gdb_compile_shlib{,_pthreads} don't support first building object
# files and then building the shlib. Therefore our hands are tied, and we
# just build the shlib in one step. This is less of a parallelization
# problem if there are multiple shlibs: Each worker can build a different
# shlib. If this proves to be a problem in practice we can enhance
# gdb_compile_shlib* then.
proc _compile_shlib { self_var static run_nr so_nr } {
upvar 1 $self_var self
set files [_gen_shlib_source self $static $run_nr $so_nr]
set source_files [lindex $files 0]
set header_files [lindex $files 1]
set extra_headers [_get_param $self(gen_shlib_extra_headers) $run_nr]
set shlib_file [_make_shlib_name self $static $run_nr $so_nr]
set compile_options "[_compile_options self] additional_flags=-DSHLIB=$so_nr"
set all_header_files $header_files
append all_header_files $extra_headers
set compile_result [_perftest_compile self $source_files $all_header_files $shlib_file shlib $compile_options]
if { $compile_result != "" } {
verbose -log "_compile_shlib failed: $compile_result"
return -1
}
return 0
}
proc _gen_tail_shlib_source { self_var static run_nr } {
upvar 1 $self_var self
verbose -log "GenPerfTest::_gen_tail_shlib_source run $run_nr"
set source_files [_get_param $self(tail_shlib_sources) $run_nr]
if { [llength $source_files] == 0 } {
return ""
}
set result ""
foreach source_name $source_files {
lappend result [_gen_shlib_common_source self $static $run_nr tail $source_name]
}
return $result
}
proc _make_tail_shlib_name { self_var static run_nr } {
upvar 1 $self_var self
set source_files [_get_param $self(tail_shlib_sources) $run_nr]
if { [llength $source_files] == 0 } {
return ""
}
return [_make_shlib_name self $static $run_nr "tail"]
}
# Helper function to compile the tail shlib, if it's specified.
proc _compile_tail_shlib { self_var static run_nr } {
upvar 1 $self_var self
set source_files [_gen_tail_shlib_source self $static $run_nr]
if { [llength $source_files] == 0 } {
return 0
}
set header_files [_get_param $self(tail_shlib_headers) $run_nr]
set shlib_file [_make_tail_shlib_name self $static $run_nr]
set compile_options [_compile_options self]
set compile_result [_perftest_compile self $source_files $header_files $shlib_file shlib $compile_options]
if { $compile_result != "" } {
verbose -log "_compile_tail_shlib failed: $compile_result"
return -1
}
verbose -log "_compile_tail_shlib failed: succeeded"
return 0
}
# Compile the pieces of the binary and possible shlibs for the test.
# The result is 0 for success, -1 for failure.
proc _compile_pieces { self_var worker_nr } {
upvar 1 $self_var self
global PERF_TEST_COMPILE_PARALLELISM
set nr_workers $PERF_TEST_COMPILE_PARALLELISM
set nr_runs [llength $self(run_names)]
set static [_static_object_files_p self]
verbose -log "_compile_pieces: static flag: $static"
file mkdir "[file dirname $self(binfile)]/pieces"
if $static {
# All the generated pieces look the same (run over run) so just
# build all the shlibs of the last run (which is the largest).
set last_run [expr $nr_runs - 1]
set nr_gen_shlibs [_get_param $self(nr_gen_shlibs) $last_run]
set object_dir [_make_object_dir_name self $static ignored]
file mkdir $object_dir
for { set so_nr $worker_nr } { $so_nr < $nr_gen_shlibs } { incr so_nr $nr_workers } {
if { [_compile_shlib self $static $last_run $so_nr] < 0 } {
return -1
}
}
# We don't shard building of tail-shlib, so only build it once.
if { $worker_nr == 0 } {
if { [_compile_tail_shlib self $static $last_run] < 0 } {
return -1
}
}
if { [_compile_binary_pieces self $worker_nr $static $last_run] < 0 } {
return -1
}
} else {
for { set run_nr 0 } { $run_nr < $nr_runs } { incr run_nr } {
set nr_gen_shlibs [_get_param $self(nr_gen_shlibs) $run_nr]
set object_dir [_make_object_dir_name self $static $run_nr]
file mkdir $object_dir
for { set so_nr $worker_nr } { $so_nr < $nr_gen_shlibs } { incr so_nr $nr_workers } {
if { [_compile_shlib self $static $run_nr $so_nr] < 0 } {
return -1
}
}
# We don't shard building of tail-shlib, so only build it once.
if { $worker_nr == 0 } {
if { [_compile_tail_shlib self $static $run_nr] < 0 } {
return -1
}
}
if { [_compile_binary_pieces self $worker_nr $static $run_nr] < 0 } {
return -1
}
}
}
return 0
}
# Main function invoked by each worker.
# This builds all the things that are possible to build in parallel,
# sharded up among all the workers.
proc compile_pieces { self_var worker_nr } {
upvar 1 $self_var self
verbose -log "GenPerfTest::compile_pieces worker $worker_nr, started [timestamp -format %c]"
verbose -log "self: [array get self]"
_verify_testcase self
if { [_compile_pieces self $worker_nr] < 0 } {
verbose -log "GenPerfTest::compile_pieces worker $worker_nr, failed [timestamp -format %c]"
return -1
}
verbose -log "GenPerfTest::compile_pieces worker $worker_nr, done [timestamp -format %c]"
return 0
}
proc _make_shlib_options { self_var static run_nr } {
upvar 1 $self_var self
set nr_gen_shlibs [_get_param $self(nr_gen_shlibs) $run_nr]
set result ""
for { set i 0 } { $i < $nr_gen_shlibs } { incr i } {
lappend result "shlib=[_make_shlib_name self $static $run_nr $i]"
}
set tail_shlib_name [_make_tail_shlib_name self $static $run_nr]
if { "$tail_shlib_name" != "" } {
lappend result "shlib=$tail_shlib_name"
}
return $result
}
proc _compile_binary { self_var static run_nr } {
upvar 1 $self_var self
set input_files [_make_binary_input_file_names self $static $run_nr]
set extra_headers [_get_param $self(binary_extra_headers) $run_nr]
set binary_file [_make_binary_name self $run_nr]
set compile_options [_compile_options self]
set shlib_options [_make_shlib_options self $static $run_nr]
if { [llength $shlib_options] > 0 } {
append compile_options " " $shlib_options
}
set compile_result [_perftest_compile self $input_files $extra_headers $binary_file executable $compile_options]
if { $compile_result != "" } {
verbose -log "_compile_binary failed: $compile_result"
return -1
}
return 0
}
# Helper function for compile.
# The result is 0 for success, -1 for failure.
proc _compile { self_var } {
upvar 1 $self_var self
set nr_runs [llength $self(run_names)]
set static [_static_object_files_p self]
verbose -log "_compile: static flag: $static"
for { set run_nr 0 } { $run_nr < $nr_runs } { incr run_nr } {
if { [_compile_binary self $static $run_nr] < 0 } {
return -1
}
}
return 0
}
# Main function to compile the test program.
# It is assumed all the pieces of the binary (all the .o's, except those
# from test-supplied sources) have already been built with compile_pieces.
# There's no need to compile any shlibs here, as compile_pieces will have
# already built them too.
# The result is 0 for success, -1 for failure.
proc compile { self_var } {
upvar 1 $self_var self
verbose -log "GenPerfTest::compile, started [timestamp -format %c]"
verbose -log "self: [array get self]"
_verify_testcase self
if { [_compile self] < 0 } {
verbose -log "GenPerfTest::compile, failed [timestamp -format %c]"
return -1
}
verbose -log "GenPerfTest::compile, done [timestamp -format %c]"
return 0
}
# Main function for running a test.
# It is assumed that the test program has already been built.
proc run { builder_exp_file_name make_config_thunk_name py_file_name test_class_name } {
verbose -log "GenPerfTest::run, started [timestamp -format %c]"
verbose -log "GenPerfTest::run, $builder_exp_file_name $make_config_thunk_name $py_file_name $test_class_name"
set testprog [file rootname $builder_exp_file_name]
# This variable is required by perftest.exp.
# This isn't the name of the test program, it's the name of the .py
# test. The harness assumes they are the same, which is not the case
# here.
global testfile
set testfile [file rootname $py_file_name]
GenPerfTest::load_test_description $builder_exp_file_name
array set testcase [$make_config_thunk_name]
PerfTest::assemble {
# Compilation is handled elsewhere.
return 0
} {
clean_restart
return 0
} {
global gdb_prompt
gdb_test_multiple "python ${test_class_name}('$testprog:$testfile', [tcl_string_list_to_python_list $testcase(run_names)], '$testcase(binfile)').run()" "run test" {
-re "Error while executing Python code.\[\r\n\]+$gdb_prompt $" {
return -1
}
-re "\[\r\n\]+$gdb_prompt $" {
}
}
return 0
}
verbose -log "GenPerfTest::run, done [timestamp -format %c]"
return 0
}
# This function is invoked by the testcase builder scripts
# (e.g., gmonster[12].exp).
# It is not invoked by the testcase runner scripts
# (e.g., gmonster[12]-*.exp).
proc standard_compile_driver { exp_file_name make_config_thunk_name } {
global GDB_PERFTEST_MODE GDB_PERFTEST_SUBMODE
if ![info exists GDB_PERFTEST_SUBMODE] {
# Probably a plain "make check-perf", nothing to do.
# Give the user a reason why we're not running this test.
verbose -log "Test must be compiled/run in separate steps."
return 0
}
switch -glob -- "$GDB_PERFTEST_MODE/$GDB_PERFTEST_SUBMODE" {
compile/gen-workers {
if { [GenPerfTest::gen_worker_files $exp_file_name] < 0 } {
fail $GDB_PERFTEST_MODE
return -1
}
pass $GDB_PERFTEST_MODE
}
compile/build-pieces {
array set testcase [$make_config_thunk_name]
global PROGRAM_NAME WORKER_NR
if { [GenPerfTest::compile_pieces testcase $WORKER_NR] < 0 } {
fail $GDB_PERFTEST_MODE
# This gdb.log lives in a different place, help the user
# find it.
set output_dir "gdb.perf/outputs"
send_user "check ${output_dir}/${PROGRAM_NAME}/${PROGRAM_NAME}-${WORKER_NR}/gdb.log\n"
return -1
}
pass $GDB_PERFTEST_MODE
}
compile/final {
array set testcase [$make_config_thunk_name]
if { [GenPerfTest::compile testcase] < 0 } {
fail $GDB_PERFTEST_MODE
return -1
}
pass $GDB_PERFTEST_MODE
}
run/* - both/* {
# Since the builder script is a .exp file living in gdb.perf
# we can get here (dejagnu will find this file for a default
# "make check-perf"). We can also get here when
# standard_run_driver loads the builder .exp file.
}
default {
error "Bad value for GDB_PERFTEST_MODE/GDB_PERFTEST_SUBMODE: $GDB_PERFTEST_MODE/$GDB_PERFTEST_SUBMODE"
}
}
return 0
}
# This function is invoked by the testcase runner scripts
# (e.g., gmonster[12]-*.exp).
# It is not invoked by the testcase builder scripts
# (e.g., gmonster[12].exp).
#
# These tests are built separately with
# "make build-perf" and run with
# "make check-perf GDB_PERFTEST_MODE=run".
# Eventually we can support GDB_PERFTEST_MODE=both, but for now we don't.
proc standard_run_driver { builder_exp_file_name make_config_thunk_name py_file_name test_class_name } {
global GDB_PERFTEST_MODE
# First step is to compile the test.
switch $GDB_PERFTEST_MODE {
compile - both {
# Here is where we'd add code to support a plain
# "make check-perf".
}
run {
}
default {
error "Bad value for GDB_PERFTEST_MODE: $GDB_PERFTEST_MODE"
}
}
# Now run the test.
switch $GDB_PERFTEST_MODE {
compile {
}
both {
# Give the user a reason why we're not running this test.
verbose -log "Test must be compiled/run in separate steps."
}
run {
if { [GenPerfTest::run $builder_exp_file_name $make_config_thunk_name $py_file_name $test_class_name] < 0 } {
fail $GDB_PERFTEST_MODE
return -1
}
pass $GDB_PERFTEST_MODE
}
}
return 0
}
}
if ![info exists PERF_TEST_COMPILE_PARALLELISM] {
set PERF_TEST_COMPILE_PARALLELISM $GenPerfTest::DEFAULT_PERF_TEST_COMPILE_PARALLELISM
}
|