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