From patchwork Thu Jun 11 01:48:10 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 7106 Received: (qmail 2552 invoked by alias); 11 Jun 2015 01:48:28 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 2542 invoked by uid 89); 11 Jun 2015 01:48:26 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.2 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_LOW, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mail-pd0-f201.google.com Received: from mail-pd0-f201.google.com (HELO mail-pd0-f201.google.com) (209.85.192.201) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Thu, 11 Jun 2015 01:48:12 +0000 Received: by pdbht2 with SMTP id ht2so5674136pdb.0 for ; Wed, 10 Jun 2015 18:48:10 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:message-id:date:subject:from:to:cc :content-type; bh=pJbIaquBMmMFugAugtwuYjfu2zCpHqsXliLu6mSdocI=; b=Yg6Xy/FOgN6ljJMfOie9Rst8iCvukxS7/wyd6NZyZBWRJx4WTFBbVDE3A24O9Tdmv/ a2SvNbRuPEji5PM46Hb75EJO1rBwcJn+Vnj4wicRayNlwDQGi7sJ2IU0SnSK2b88juX/ Hj9xVpiN6CXu8NIFiQhTsf+nk64F2EdmKvUK5q+AK3IIVaCmTS2TACJSA/isOqTQJNGX Hpp9ilNv5O/hmjvSNkjHf36PA28vxjLJvnCUidaYAOiJ7opu5CW2Orshr8pVczIhR7Tk JWXSo5fBu2BjsXja+e4pCor1OMToI84r7GlYGomv3rcDzzQz38sz8RwTVX0fLX8ilrC+ 5d1w== X-Gm-Message-State: ALoCoQnIY05CsjAhdBTLywuBccj1Jv7oAeShgoIq7saSi0eCYSHtcKv+Y6UT0ruv6oclOhwxJvUVpF+SC8TLsMjJgVu6+9SOf3dItrev8cR4QhGRm4ORsWOLTOMgQfwmZS1+x1q6rfBJmeZVS1hT4MDR7TsoaJKH3Ud3AX8Tv2QqHymamAHv9LE= MIME-Version: 1.0 X-Received: by 10.66.121.79 with SMTP id li15mr7806125pab.12.1433987290605; Wed, 10 Jun 2015 18:48:10 -0700 (PDT) Message-ID: Date: Thu, 11 Jun 2015 01:48:10 +0000 Subject: [RFC] Monster testcase generator for performance testsuite From: Doug Evans To: gdb-patches@sourceware.org Cc: keiths@redhat.com, yao.qi@linaro.org X-IsSubscribed: yes Hi. fyi, here's the latest version of my monster testcase generator. I added a README so that there's something to read in the patch. However, I think a better place for most of it is the wiki. I hope to submit a real patch soon. I'm sending this now because it's a good time to give another heads up. Last discussion of the patch was here. https://sourceware.org/ml/gdb-patches/2015-01/msg00013.html # The name of python file on build. @@ -42,14 +46,13 @@ namespace eval PerfTest { # actual compilation. Return zero if compilation is successful, # otherwise return non-zero. proc compile {body} { - global GDB_PERFTEST_MODE - - if { [info exists GDB_PERFTEST_MODE] - && [string compare $GDB_PERFTEST_MODE "run"] } { - return [uplevel 2 $body] - } + return [uplevel 2 $body] + } - return 0 + # Run the startup code. Return zero if startup is successful, + # otherwise return non-zero. + proc startup {body} { + return [uplevel 2 $body] } # Start up GDB. @@ -57,7 +60,8 @@ namespace eval PerfTest { uplevel 2 $body } - # Run the performance test. + # Run the performance test. Return zero if the run is successful, + # otherwise return non-zero. proc run {body} { global timeout global GDB_PERFTEST_TIMEOUT @@ -68,36 +72,56 @@ namespace eval PerfTest { } else { set timeout 3000 } - uplevel 2 $body + set result [uplevel 2 $body] set timeout $oldtimeout + return $result } # The top-level interface to PerfTest. # COMPILE is the tcl code to generate and compile source files. - # Return zero if compilation is successful, otherwise return - # non-zero. # STARTUP is the tcl code to start up GDB. # RUN is the tcl code to drive GDB to do some operations. + # Each of COMPILE, STARTUP, and RUN return zero if successful, and + # non-zero if there's a failure. + proc assemble {compile startup run} { global GDB_PERFTEST_MODE - if { [eval compile {$compile}] } { - untested "Could not compile source files." + if ![info exists GDB_PERFTEST_MODE] { return } + if { [string compare $GDB_PERFTEST_MODE "run"] != 0 } { + if { [eval compile {$compile}] } { + untested "Could not compile source files." + return + } + } + # Don't execute the run if GDB_PERFTEST_MODE=compile. - if { [info exists GDB_PERFTEST_MODE] - && [string compare $GDB_PERFTEST_MODE "compile"] == 0} { + if { [string compare $GDB_PERFTEST_MODE "compile"] == 0} { + return + } + + verbose -log "PerfTest::assemble, startup ..." + + if [eval startup {$startup}] { + fail "startup" return } - eval $startup + verbose -log "PerfTest::assemble, done startup" _setup_perftest - eval run {$run} + verbose -log "PerfTest::assemble, run ..." + + if [eval run {$run}] { + fail "run" + } + + verbose -log "PerfTest::assemble, run complete." _teardown_perftest } @@ -109,11 +133,9 @@ proc skip_perf_tests { } { global GDB_PERFTEST_MODE if [info exists GDB_PERFTEST_MODE] { - if { "$GDB_PERFTEST_MODE" != "compile" && "$GDB_PERFTEST_MODE" != "run" && "$GDB_PERFTEST_MODE" != "both" } { - # GDB_PERFTEST_MODE=compile|run|both is allowed. error "Unknown value of GDB_PERFTEST_MODE." return 1 } @@ -123,3 +145,1248 @@ proc skip_perf_tests { } { return 1 } + +# Given a list of tcl strings, return the same list as the text form of a +# python list. + +proc tcl_string_list_to_python_list { l } { + proc quote { text } { + return "\"$text\"" + } + set quoted_list "" + foreach elm $l { + lappend quoted_list [quote $elm] + } + return "([join $quoted_list {, }])" +} + +# 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) inline methods +# 3) anonymous namespaces +# +# 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 + + # List of pairs of class depth and number of classes at that depth. + # By "depth" here we mean nesting within a namespace. + # E.g., + # class foo {}; + # namespace n { class foo {}; class bar {}; } + # would be represented as { { 0 1 } { 1 2 } }. + # This is only used if the selected language permits it. + set DEFAULT_CLASS_SPECS {} + + # Number of members in each class. + # This is only used if classes are enabled. + set DEFAULT_NR_MEMBERS 0 + + # Number of static members in each class. + # This is only used if classes are enabled. + set DEFAULT_NR_STATIC_MEMBERS 0 + + # Number of methods in each class. + # This is only used if classes are enabled. + set DEFAULT_NR_METHODS 0 + + # Number of static methods in each class. + # This is only used if classes are enabled. + set DEFAULT_NR_STATIC_METHODS 0 + + set suffixes(c) "c" + set 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 + set testcase(nr_members) $GenPerfTest::DEFAULT_NR_MEMBERS + set testcase(nr_static_members) $GenPerfTest::DEFAULT_NR_STATIC_MEMBERS + set testcase(nr_methods) $GenPerfTest::DEFAULT_NR_METHODS + set testcase(nr_static_methods) $GenPerfTest::DEFAULT_NR_STATIC_METHODS + + # 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 + # TODO(dje): Do we want *_headers here? + 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 + nr_members nr_static_members + nr_methods nr_static_methods + } + 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 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 + + # 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] + foreach elm $class_specs { + if { [llength $elm] != 2 } { + error "Bad class spec: $elm" + } + if { [lindex $elm 1] > 0 } { + return 1 + } + } + return 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" + } + } + + # CU_NR is either the compilation unit number or "main". + # RUN_NR is ignored if STATIC is non-zero. + + proc _make_binary_source_name { self_var static run_nr cu_nr } { + upvar 1 $self_var self + set source_suffix $GenPerfTest::suffixes($self(language)) + if { !$static } { + set run_name [_get_param $self(run_names) $run_nr] + set source_name "${run_name}-${cu_nr}.$source_suffix" + } else { + set source_name "$self(name)-${cu_nr}.$source_suffix" + } + return "[_make_object_dir_name self $static $run_nr]/[_convert_spaces $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_binary_source_name self $static $run_nr $cu_nr] + return [file rootname $source_name].o + } + + # CU_NAME is either "CU-number.cc" (with suffix) or a name from + # gen_shlib_extra_sources or tail_shlib_sources. + + proc _make_shlib_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 "$self(name)-${run_name}-lib${so_nr}-${cu_name}" + } else { + set source_name "$self(name)-lib${so_nr}-${cu_name}" + } + return "[_make_object_dir_name self $static $run_nr]/[_convert_spaces $source_name]" + } + + # 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 _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 _write_classes { self_var f run_nr cu_nr } { + upvar 1 $self_var self + set class_specs [_get_param $self(class_specs) $run_nr] + set nr_members [_get_param $self(nr_members) $run_nr] + set nr_static_members [_get_param $self(nr_static_members) $run_nr] + set nr_methods [_get_param $self(nr_methods) $run_nr] + set nr_static_methods [_get_param $self(nr_static_methods) $run_nr] + foreach spec $class_specs { + set depth [lindex $spec 0] + set nr_classes [lindex $spec 1] + puts $f "" + for { set i 0 } { $i < $depth } { incr i } { + puts $f "namespace ns_${i}" + puts $f "\{" + } + for { set c 0 } { $c < $nr_classes } { incr c } { + set class_name "class_${cu_nr}_${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);" + } + puts $f "\};" + _write_static_members self $f $run_nr $class_name + _write_methods self $f $run_nr $class_name + _write_static_methods self $f $run_nr $class_name + } + for { set i 0 } { $i < $depth } { incr i } { + puts $f "\}" + } + } + } + + proc _write_static_members { self_var f run_nr class_name } { + upvar 1 $self_var self + puts $f "" + set nr_static_members [_get_param $self(nr_static_members) $run_nr] + # 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 run_nr class_name } { + upvar 1 $self_var self + set nr_methods [_get_param $self(nr_methods) $run_nr] + 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 run_nr class_name } { + upvar 1 $self_var self + set nr_static_methods [_get_param $self(nr_static_methods) $run_nr] + 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_binary_compunit_source { self_var static run_nr cu_nr } { + upvar 1 $self_var self + set source_file [_make_binary_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] + _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_classes self $f $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 object0 } { source10 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 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 $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_suffix $GenPerfTest::suffixes($self(language)) + set source_file [_make_shlib_source_name self $static $run_nr $so_nr "${cu_nr}.${source_suffix}"] + set f [_create_file self $source_file] + _write_intro self $f + _write_includes self $f [_get_param $self(gen_shlib_extra_headers) $run_nr] + _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_classes self $f $run_nr $cu_nr + } + close $f + return $source_file + } + + # 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_source_name self $static $run_nr $so_nr $source_name] + file copy -force "$srcdir/gdb.perf/$source_name" ${source_file} + return $source_file + } + + 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 result "" + set nr_compunits [_get_param $self(nr_compunits) $run_nr] + for { set cu_nr 0 } { $cu_nr < $nr_compunits } { incr cu_nr } { + lappend result [_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 result [_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 $result + } + + # 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 + } + 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 header_files [_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 object_file [lindex $elm 1] + set compile_result [_perftest_compile self $source_file $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 source_files [_gen_shlib_source self $static $run_nr $so_nr] + set header_files [_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 compile_result [_perftest_compile self $source_files $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 header_files [_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 $header_files $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 + set output_dir "gdb.perf/pieces" + 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. + send_user "check ${output_dir}/${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 +} diff --git a/gdb/testsuite/Makefile.in b/gdb/testsuite/Makefile.in index c064f06..ffda7b9 100644 --- a/gdb/testsuite/Makefile.in +++ b/gdb/testsuite/Makefile.in @@ -227,16 +227,48 @@ do-check-parallel: $(TEST_TARGETS) @GMAKE_TRUE@check/%.exp: @GMAKE_TRUE@ -mkdir -p outputs/$* -@GMAKE_TRUE@ @$(DO_RUNTEST) GDB_PARALLEL=yes --outdir=outputs/$* $*.exp $(RUNTESTFLAGS) +@GMAKE_TRUE@ @$(DO_RUNTEST) GDB_PARALLEL=. --outdir=outputs/$* $*.exp $(RUNTESTFLAGS) check/no-matching-tests-found: @echo "" @echo "No matching tests found." @echo "" +# Utility rule invoked by step 2 of the build-perf rule. +@GMAKE_TRUE@workers/%.worker: +@GMAKE_TRUE@ mkdir -p gdb.perf/outputs/$* +@GMAKE_TRUE@ $(DO_RUNTEST) --status --outdir=gdb.perf/outputs/$* lib/build-piece.exp WORKER=$* GDB_PARALLEL=gdb.perf $(RUNTESTFLAGS) GDB_PERFTEST_MODE=compile GDB_PERFTEST_SUBMODE=build-pieces + +# Utility rule to build tests that support it in parallel. +# The build is broken into 3 steps distinguished by GDB_PERFTEST_SUBMODE: +# gen-workers, build-pieces, final. +# +# GDB_PERFTEST_MODE appears *after* RUNTESTFLAGS here because we don't want +# anything in RUNTESTFLAGS to override it. +# +# We don't delete the outputs directory here as these programs can take +# awhile to build, and perftest.exp has support for deciding whether to +# recompile them. If you want to remove these directories, make clean. +# +# The point of step 1 is to construct the set of worker tasks for step 2. +# All of the information needed by build-piece.exp is contained in the name +# of the generated .worker file. +@GMAKE_TRUE@build-perf: $(abs_builddir)/site.exp +@GMAKE_TRUE@ rm -rf gdb.perf/workers +@GMAKE_TRUE@ mkdir -p gdb.perf/workers +@GMAKE_TRUE@ @: Step 1: Generate the build .worker files. +@GMAKE_TRUE@ $(DO_RUNTEST) --status --directory=gdb.perf --outdir gdb.perf/workers GDB_PARALLEL=gdb.perf $(RUNTESTFLAGS) GDB_PERFTEST_MODE=compile GDB_PERFTEST_SUBMODE=gen-workers +@GMAKE_TRUE@ @: Step 2: Compile the pieces. Here is the build parallelism. +@GMAKE_TRUE@ $(MAKE) $$(cd gdb.perf && echo workers/*/*.worker) +@GMAKE_TRUE@ @: Step 3: Do the final link. +@GMAKE_TRUE@ $(DO_RUNTEST) --status --directory=gdb.perf --outdir gdb.perf GDB_PARALLEL=gdb.perf $(RUNTESTFLAGS) GDB_PERFTEST_MODE=compile GDB_PERFTEST_SUBMODE=final + +# The default is to both compile and run the tests. +GDB_PERFTEST_MODE = both + check-perf: all $(abs_builddir)/site.exp @if test ! -d gdb.perf; then mkdir gdb.perf; fi - $(DO_RUNTEST) --directory=gdb.perf --outdir gdb.perf GDB_PERFTEST_MODE=both $(RUNTESTFLAGS) + $(DO_RUNTEST) --directory=gdb.perf --outdir gdb.perf GDB_PERFTEST_MODE=$(GDB_PERFTEST_MODE) $(RUNTESTFLAGS) force:; @@ -245,6 +277,7 @@ clean mostlyclean: -rm -f core.* *.tf *.cl tracecommandsscript copy1.txt zzz-gdbscript -rm -f *.dwo *.dwp -rm -rf outputs temp cache + -rm -rf gdb.perf/workers gdb.perf/outputs gdb.perf/temp gdb.perf/cache -rm -f read1.so expect-read1 if [ x"${ALL_SUBDIRS}" != x ] ; then \ for dir in ${ALL_SUBDIRS}; \ diff --git a/gdb/testsuite/gdb.base/watchpoint.exp b/gdb/testsuite/gdb.base/watchpoint.exp index b2924d7..fcc9a8d 100644 --- a/gdb/testsuite/gdb.base/watchpoint.exp +++ b/gdb/testsuite/gdb.base/watchpoint.exp @@ -464,12 +464,11 @@ proc test_complex_watchpoint {} { pass $test } -re "can't compute CFA for this frame.*\r\n$gdb_prompt $" { - global compiler_info no_hw + global no_hw # GCC < 4.5.0 does not get LOCATIONS_VALID set by dwarf2read.c. # Therefore epilogue unwinder gets applied which is # incompatible with dwarf2_frame_cfa. - verbose -log "compiler_info: $compiler_info" if {$no_hw && ([test_compiler_info {gcc-[0-3]-*}] || [test_compiler_info {gcc-4-[0-4]-*}])} { xfail "$test (old GCC has broken watchpoints in epilogues)" diff --git a/gdb/testsuite/gdb.cp/temargs.exp b/gdb/testsuite/gdb.cp/temargs.exp index e5aff51..f086a63 100644 --- a/gdb/testsuite/gdb.cp/temargs.exp +++ b/gdb/testsuite/gdb.cp/temargs.exp @@ -34,7 +34,6 @@ if {![runto_main]} { # NOTE: prepare_for_testing calls get_compiler_info, which we need # for the test_compiler_info calls. # gcc 4.4 and earlier don't emit enough info for some of our template tests. -verbose -log "compiler_info: $compiler_info" set have_older_template_gcc 0 set have_pr_41736_fixed 1 set have_pr_45024_fixed 1 diff --git a/gdb/testsuite/gdb.perf/README b/gdb/testsuite/gdb.perf/README new file mode 100644 index 0000000..86fb9bd --- /dev/null +++ b/gdb/testsuite/gdb.perf/README @@ -0,0 +1,192 @@ +The GDB Performance Testsuite +============================= + +This README contains notes on hacking on GDB's performance testsuite. +For notes on GDB's regular testsuite or how to run the performance testsuite, +see ../README. + +Generated tests +*************** + +The testcase generator lets us easily test GDB on large programs. +The "monster" tests are mocks of real programs where GDB's +performance has been a problem. Often it is difficult to build +these monster programs, but when measuring performance one doesn't +need the "real" program, all one needs is something that looks like +the real program along the axis one is measuring; for example, the +number of CUs (compilation units). + +Structure of generated tests +**************************** + +Generated tests consist of a binary and potentially any number of +shared libraries. One of these shared libraries, called "tail", is +special. It is used to provide mocks of system provided code, and +contains no generated code. Typically system-provided libraries +are searched last which can have significant performance consequences, +so we provide a means to exercise that. + +The binary and the generated shared libraries can have a mix of +manually written and generated code. Manually written code is +specified with the {binary,gen_shlib}_extra_sources config parameters, +which are lists of source files in testsuite/gdb.perf. Generated +files are controlled with various configuration knobs. + +Once a large test program is built, it makes sense to use it as much +as possible (i.e., with multiple tests). Therefore perf data collection +for generated tests is split into two passes: the first pass builds +all the generated tests, and the second pass runs all the performance +tests. The first pass is called "build-perf" and the second pass is +called "check-perf". See ../README for instructions on running the tests. + +Generated test directory layout +******************************* + +All output lives under testsuite/gdb.perf in the build directory. + +Because some of the tests can get really large (and take potentially +minutes to compile), parallelism is built into their compilation. +Note however that we don't run the tests in parallel as it can skew +the results. + +To keep things simple and stay consistent, we use the same +mechanism used by "make check-parallel". There is one catch: we need +one .exp for each "worker" but the .exp file must come from the source +tree. To avoid generating .exp files for each worker we invoke +lib/build-piece.exp for each worker with different arguments. +The file build.piece.exp lives in "lib" to prevent dejagnu from finding +it when it goes to look for .exp scripts to run. + +Another catch is that each parallel build worker needs its own directory +so that their gdb.{log,sum} files don't collide. On the other hand +its easier if their output (all the object files and shared libraries) +are in the same directory. + +The above considerations yield the resulting layout: + +$objdir/testsuite/gdb.perf/ + + gdb.log, gdb.sum: result of doing final link and running tests + + workers/ + + gdb.log, gdb.sum: result of gen-workers step + + $program_name/ + + ${program_name}-0.worker + ... + ${program_name}-N.worker: input to build-pieces step + + outputs/ + + ${program_name}/ + + ${program_name}-0/ + ... + ${program_name}-N/ + + gdb.log, gdb.sum: for each build-piece worker + + pieces/ + + generated sources, object files, shlibs + + ${run_name_1}: binary for test config #1 + ... + ${run_name_N}: binary for test config #N + +Generated test configuration knobs +********************************** + +The monster program generator provides various knobs for building various +kinds of monster programs. For a list of the knobs see function +GenPerfTest::init_testcase in testsuite/lib/perftest.exp. +Most knobs are self-explanatory. +Here is a description of the less obvious ones. + +binary_extra_sources + + This is the list of non-machine generated sources that go + into the test binary. There must be at least one: the one + with main. + +class_specs + + List of pairs of class depth and number of classes at that depth. + By "depth" here we mean nesting within a namespace. + + E.g., + class foo {}; + namespace n { class foo {}; class bar {}; } + would be represented as { { 0 1 } { 1 2 } }. + + The naming of each namespace is "ns_". + The naming of each class is "class__", + where is the number of the compilation unit the + class is defined in. + There is currently no support for defining classes in headers + (something to be added when needed). + + There's currently no support for nesting classes in classes. + +Misc. configuration knobs +************************* + +These knobs control building or running of the test and are specified +like any global Tcl variable. + +CAT_PROGRAM + + Default is /bin/cat, you shouldn't need to change this. + +SHA1SUM_PROGRAM + + Default is /usr/bin/sha1sum. + +PERF_TEST_COMPILE_PARALLELISM + + An integer, specifies the amount of parallelism in the builds. + Akin to make's -j flag. The default is 10. + +Writing a generated test program +******************************** + +The best way to write a generated test program is to take an existing +one as boilerplate. Two good examples are gmonster1.exp and gmonster2.exp. +gmonster1.exp builds a big binary with various custom manually written +code, and gmonster2 is (essentially) the equivalent binary split up over +several shared libraries. + +Writing a performance test that uses a generated program +******************************************************** + +The best way to write a test is to take an existing one as boilerplate. +Good examples are gmonster1-*.exp and gmonster2-*.exp. + +The naming used thus far is that "foo.exp" builds the test program +and there is one "foo-bar.exp" file for each performance test +that uses test program "foo". + +In addition to writing the test driver .exp script, one must also +write a python script that is used to run the test. +This contents of this script is defined by the performance testsuite +harness. It defines a class, which is a subclass of one of the +classes in gdb.perf/lib/perftest/perftest.py. +See gmonster-null-lookup.py for an example. + +Note: Since gmonster1 and gmonster2 are treated as being variations of +the same program, each test shares the same python script. +E.g., gmonster1-null-lookup.exp and gmonster2-null-lookup.exp +both use gmonster-null-lookup.py. + +Running performance tests for generated programs +************************************************ + +There are two steps: build and run. + +Example: + +bash$ make -j10 build-perf RUNTESTFLAGS="gmonster1.exp" +bash$ make -j10 check-perf RUNTESTFLAGS="gmonster1-null-lookup.exp" \ + GDB_PERFTEST_MODE=run diff --git a/gdb/testsuite/gdb.perf/backtrace.exp b/gdb/testsuite/gdb.perf/backtrace.exp index a88064b..0ae4b5b 100644 --- a/gdb/testsuite/gdb.perf/backtrace.exp +++ b/gdb/testsuite/gdb.perf/backtrace.exp @@ -58,9 +58,12 @@ PerfTest::assemble { gdb_breakpoint "fun2" gdb_continue_to_breakpoint "fun2" + + return 0 } { global BACKTRACE_DEPTH gdb_test "python BackTrace\($BACKTRACE_DEPTH\).run()" + return 0 } diff --git a/gdb/testsuite/gdb.perf/disassemble.exp b/gdb/testsuite/gdb.perf/disassemble.exp index fe943d8..67e9815 100644 --- a/gdb/testsuite/gdb.perf/disassemble.exp +++ b/gdb/testsuite/gdb.perf/disassemble.exp @@ -52,6 +52,9 @@ PerfTest::assemble { if ![runto_main] { return -1 } + + return 0 } { gdb_test "python Disassemble\(\).run()" + return 0 } diff --git a/gdb/testsuite/gdb.perf/gm-hello.cc b/gdb/testsuite/gdb.perf/gm-hello.cc new file mode 100644 index 0000000..80a7dbc --- /dev/null +++ b/gdb/testsuite/gdb.perf/gm-hello.cc @@ -0,0 +1,25 @@ +/* Copyright (C) 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 . */ + +#include +#include "gm-utils.h" + +#ifdef SHLIB +#define HELLO CONCAT2 (hello_, SHLIB) +#else +#define HELLO hello +#endif + +std::string HELLO ("Hello."); diff --git a/gdb/testsuite/gdb.perf/gm-pervasive-typedef.cc b/gdb/testsuite/gdb.perf/gm-pervasive-typedef.cc new file mode 100644 index 0000000..e7712dc --- /dev/null +++ b/gdb/testsuite/gdb.perf/gm-pervasive-typedef.cc @@ -0,0 +1,30 @@ +/* Copyright (C) 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 . */ + +#include "gm-pervasive-typedef.h" + +my_int use_of_my_int; + +void +call_use_my_int_1 (my_int x) +{ + use_of_my_int = use_my_int (x); +} + +void +call_use_my_int () +{ + call_use_my_int_1 (42); +} diff --git a/gdb/testsuite/gdb.perf/gm-pervasive-typedef.h b/gdb/testsuite/gdb.perf/gm-pervasive-typedef.h new file mode 100644 index 0000000..6b65c27 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gm-pervasive-typedef.h @@ -0,0 +1,30 @@ +/* Copyright (C) 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 . */ + +/* This file is used to create the conditions for the perf regression + in pr 16253. */ + +#ifndef GM_PERVASIVE_TYPEDEF_H +#define GM_PERVASIVE_TYPEDEF_H + +typedef int my_int; + +static my_int +use_my_int (my_int x) +{ + return x + 1; +} + +#endif /* GM_PERVASIVE_TYPEDEF_H */ diff --git a/gdb/testsuite/gdb.perf/gm-std.cc b/gdb/testsuite/gdb.perf/gm-std.cc new file mode 100644 index 0000000..89ac500 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gm-std.cc @@ -0,0 +1,36 @@ +/* Copyright (C) 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 . */ + +#include +#include "gm-std.h" + +namespace gm_std +{ + +ostream cerr; + +void +init () +{ + cerr.stream = &std::cerr; +} + +template class basic_ostream; + +template +ostream& +operator<< (ostream& out, const char* s); + +} diff --git a/gdb/testsuite/gdb.perf/gm-std.h b/gdb/testsuite/gdb.perf/gm-std.h new file mode 100644 index 0000000..8bda713 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gm-std.h @@ -0,0 +1,57 @@ +/* Copyright (C) 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 . */ + +#ifndef GM_STD_H +#define GM_STD_H + +#include + +namespace gm_std +{ + +// Mock std::cerr, so we don't have to worry about the vagaries of the +// system-provided one. E.g., gcc pr 65669. +// This contains just enough to exercise what we want to. +template + class basic_ostream +{ + public: + std::ostream *stream; +}; + +template + basic_ostream& +operator<< (basic_ostream& out, const char* s) +{ + (*out.stream) << s; + return out; +} + +typedef basic_ostream ostream; + +// Inhibit implicit instantiations for required instantiations, +// which are defined via explicit instantiations elsewhere. +extern template class basic_ostream; +extern template ostream& operator<< (ostream&, const char*); + +extern ostream cerr; + +// Call this from main so we don't have to do the same tricks that +// libstcd++ does with ios init'n. +extern void init (); + +} + +#endif /* GM_STD_H */ diff --git a/gdb/testsuite/gdb.perf/gm-use-cerr.cc b/gdb/testsuite/gdb.perf/gm-use-cerr.cc new file mode 100644 index 0000000..5ef453b --- /dev/null +++ b/gdb/testsuite/gdb.perf/gm-use-cerr.cc @@ -0,0 +1,29 @@ +/* Copyright (C) 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 . */ + +#include "gm-std.h" +#include "gm-utils.h" + +#ifdef SHLIB +#define WRITE_CERR XCONCAT2 (write_cerr_, SHLIB) +#else +#define WRITE_CERR write_cerr +#endif + +void +WRITE_CERR () +{ + gm_std::cerr << "Yikes!\n"; +} diff --git a/gdb/testsuite/gdb.perf/gm-utils.h b/gdb/testsuite/gdb.perf/gm-utils.h new file mode 100644 index 0000000..f95ae06 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gm-utils.h @@ -0,0 +1,25 @@ +/* Copyright (C) 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 . */ + +#ifndef GM_UTILS_H +#define GM_UTILS_H + +/* Names borrowed from include/symcat.h. */ +#define CONCAT2(a,b) a ## b +#define XCONCAT2(a,b) CONCAT2 (a, b) +#define STRINGX(s) #s +#define XSTRING(s) STRINGX (s) + +#endif /* GM_UTILS_H */ diff --git a/gdb/testsuite/gdb.perf/gmonster-backtrace.py b/gdb/testsuite/gdb.perf/gmonster-backtrace.py new file mode 100644 index 0000000..b64df0b --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster-backtrace.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 . + +# Measure performance of selecting a file to debug and then running to main. + +from perftest import perftest +from perftest import measure +from perftest import utils + +class GmonsterRuntoMain(perftest.TestCaseWithBasicMeasurements): + def __init__(self, name, run_names, binfile): + # We want to measure time in this test. + super(GmonsterRuntoMain, self).__init__(name) + self.run_names = run_names + self.binfile = binfile + + def warm_up(self): + pass + + def _doit(self, binfile): + utils.select_file(binfile) + utils.runto_main() + + def execute_test(self): + for run in self.run_names: + this_run_binfile = "%s-%s" % (self.binfile, + utils.convert_spaces(run)) + iteration = 5 + while iteration > 0: + func = lambda: self._doit(this_run_binfile) + self.measure.measure(func, run) + iteration -= 1 diff --git a/gdb/testsuite/gdb.perf/gmonster-null-lookup.py b/gdb/testsuite/gdb.perf/gmonster-null-lookup.py new file mode 100644 index 0000000..9bb839e --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster-null-lookup.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 . + +# Test handling of lookup of a symbol that doesn't exist. + +from perftest import perftest +from perftest import measure +from perftest import utils + +class NullLookup(perftest.TestCaseWithBasicMeasurements): + def __init__(self, name, run_names, binfile): + # We want to measure time in this test. + super(NullLookup, self).__init__(name) + self.run_names = run_names + self.binfile = binfile + + def warm_up(self): + pass + + def execute_test(self): + for run in self.run_names: + this_run_binfile = "%s-%s" % (self.binfile, + utils.convert_spaces(run)) + utils.select_file(this_run_binfile) + utils.runto_main() + utils.safe_execute("mt expand-symtabs") + iteration = 5 + while iteration > 0: + utils.safe_execute("mt flush-symbol-cache") + func = lambda: utils.safe_execute("p symbol_not_found") + self.measure.measure(func, run) + iteration -= 1 diff --git a/gdb/testsuite/gdb.perf/gmonster-pervasive-typedef.py b/gdb/testsuite/gdb.perf/gmonster-pervasive-typedef.py new file mode 100644 index 0000000..e22562a --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster-pervasive-typedef.py @@ -0,0 +1,43 @@ +# Copyright (C) 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 . + +# Exercise the perf issue from pr 16253. + +from perftest import perftest +from perftest import measure +from perftest import utils + +class PervasiveTypedef(perftest.TestCaseWithBasicMeasurements): + def __init__(self, name, run_names, binfile): + # We want to measure time in this test. + super(PervasiveTypedef, self).__init__(name) + self.run_names = run_names + self.binfile = binfile + + def warm_up(self): + pass + + def func(self): + utils.select_file(self.this_run_binfile) + utils.safe_execute("ptype call_use_my_int_1") + + def execute_test(self): + for run in self.run_names: + self.this_run_binfile = "%s-%s" % (self.binfile, + utils.convert_spaces(run)) + iteration = 5 + while iteration > 0: + self.measure.measure(self.func, run) + iteration -= 1 diff --git a/gdb/testsuite/gdb.perf/gmonster-print-cerr.py b/gdb/testsuite/gdb.perf/gmonster-print-cerr.py new file mode 100644 index 0000000..30e86e5 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster-print-cerr.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 . + +# Test printing of std::cerr. + +from perftest import perftest +from perftest import measure +from perftest import utils + +class PrintCerr(perftest.TestCaseWithBasicMeasurements): + def __init__(self, name, run_names, binfile): + # We want to measure time in this test. + super(PrintCerr, self).__init__(name) + self.run_names = run_names + self.binfile = binfile + + def warm_up(self): + pass + + def execute_test(self): + for run in self.run_names: + this_run_binfile = "%s-%s" % (self.binfile, + utils.convert_spaces(run)) + utils.select_file(this_run_binfile) + utils.runto_main() + #utils.safe_execute("mt expand-symtabs") + iteration = 5 + while iteration > 0: + utils.safe_execute("mt flush-symbol-cache") + func = lambda: utils.safe_execute("print gm_std::cerr") + self.measure.measure(func, run) + iteration -= 1 diff --git a/gdb/testsuite/gdb.perf/gmonster-ptype-string.py b/gdb/testsuite/gdb.perf/gmonster-ptype-string.py new file mode 100644 index 0000000..d39f4ce --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster-ptype-string.py @@ -0,0 +1,45 @@ +# Copyright (C) 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 . + +# Measure speed of ptype of a std::string object. + +from perftest import perftest +from perftest import measure +from perftest import utils + +class GmonsterPtypeString(perftest.TestCaseWithBasicMeasurements): + def __init__(self, name, run_names, binfile): + # We want to measure time in this test. + super(GmonsterPtypeString, self).__init__(name) + self.run_names = run_names + self.binfile = binfile + + def warm_up(self): + pass + + def execute_test(self): + for run in self.run_names: + this_run_binfile = "%s-%s" % (self.binfile, + utils.convert_spaces(run)) + utils.select_file(this_run_binfile) + utils.runto_main() + utils.safe_execute("mt expand-symtabs") + iteration = 5 + while iteration > 0: + utils.safe_execute("mt flush-symbol-cache") + func1 = lambda: utils.safe_execute("ptype hello") + func = lambda: utils.run_n_times(2, func1) + self.measure.measure(func, run) + iteration -= 1 diff --git a/gdb/testsuite/gdb.perf/gmonster-runto-main.py b/gdb/testsuite/gdb.perf/gmonster-runto-main.py new file mode 100644 index 0000000..b64df0b --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster-runto-main.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 . + +# Measure performance of selecting a file to debug and then running to main. + +from perftest import perftest +from perftest import measure +from perftest import utils + +class GmonsterRuntoMain(perftest.TestCaseWithBasicMeasurements): + def __init__(self, name, run_names, binfile): + # We want to measure time in this test. + super(GmonsterRuntoMain, self).__init__(name) + self.run_names = run_names + self.binfile = binfile + + def warm_up(self): + pass + + def _doit(self, binfile): + utils.select_file(binfile) + utils.runto_main() + + def execute_test(self): + for run in self.run_names: + this_run_binfile = "%s-%s" % (self.binfile, + utils.convert_spaces(run)) + iteration = 5 + while iteration > 0: + func = lambda: self._doit(this_run_binfile) + self.measure.measure(func, run) + iteration -= 1 diff --git a/gdb/testsuite/gdb.perf/gmonster1-backtrace.exp b/gdb/testsuite/gdb.perf/gmonster1-backtrace.exp new file mode 100644 index 0000000..d7df396 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster1-backtrace.exp @@ -0,0 +1,25 @@ +# Copyright (C) 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 . + +# Measure performance of a backtrace in a large executable. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster1.exp gmonster-backtrace.py make_testcase_config GmonsterBacktrace diff --git a/gdb/testsuite/gdb.perf/gmonster1-null-lookup.exp b/gdb/testsuite/gdb.perf/gmonster1-null-lookup.exp new file mode 100644 index 0000000..5f48c79 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster1-null-lookup.exp @@ -0,0 +1,25 @@ +# Copyright (C) 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 . + +# Measure speed of lookup of a symbol that doesn't exist. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster1.exp make_testcase_config gmonster-null-lookup.py NullLookup diff --git a/gdb/testsuite/gdb.perf/gmonster1-pervasive-typedef.exp b/gdb/testsuite/gdb.perf/gmonster1-pervasive-typedef.exp new file mode 100644 index 0000000..ab68a3e --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster1-pervasive-typedef.exp @@ -0,0 +1,27 @@ +# Copyright (C) 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 . + +# Measure the speed of "ptype func" where a parameter of the function is a +# typedef used pervasively. This exercises the perf regression introduced by +# the original patch to pr 16253. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster1.exp make_testcase_config gmonster-pervasive-typedef.py PervasiveTypedef diff --git a/gdb/testsuite/gdb.perf/gmonster1-print-cerr.exp b/gdb/testsuite/gdb.perf/gmonster1-print-cerr.exp new file mode 100644 index 0000000..20d2716 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster1-print-cerr.exp @@ -0,0 +1,25 @@ +# Copyright (C) 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 . + +# Measure speed of printing std::cerr. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster1.exp make_testcase_config gmonster-print-cerr.py PrintCerr diff --git a/gdb/testsuite/gdb.perf/gmonster1-ptype-string.exp b/gdb/testsuite/gdb.perf/gmonster1-ptype-string.exp new file mode 100644 index 0000000..8eadbe7 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster1-ptype-string.exp @@ -0,0 +1,25 @@ +# Copyright (C) 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 . + +# Measure speed of ptype on a simple class in a library that is searched late. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster1.exp make_testcase_config gmonster-ptype-string.py GmonsterPtypeString diff --git a/gdb/testsuite/gdb.perf/gmonster1-runto-main.exp b/gdb/testsuite/gdb.perf/gmonster1-runto-main.exp new file mode 100644 index 0000000..665f94c --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster1-runto-main.exp @@ -0,0 +1,25 @@ +# Copyright (C) 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 . + +# Measure performance of selecting a file to debug and then running to main. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster1.exp make_testcase_config gmonster-runto-main.py GmonsterRuntoMain diff --git a/gdb/testsuite/gdb.perf/gmonster1.cc b/gdb/testsuite/gdb.perf/gmonster1.cc new file mode 100644 index 0000000..4ae9837 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster1.cc @@ -0,0 +1,24 @@ +/* Copyright (C) 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 . */ + +#include "gm-std.h" + +int +main () +{ + gm_std::init (); + + return 0; +} diff --git a/gdb/testsuite/gdb.perf/gmonster1.exp b/gdb/testsuite/gdb.perf/gmonster1.exp new file mode 100644 index 0000000..e951c74 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster1.exp @@ -0,0 +1,86 @@ +# Copyright (C) 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 . + +# Perftest description file for building the "gmonster1" benchmark. +# Where does the name come from? The benchmark is derived from one of the +# monster programs at Google. +# +# Perftest descriptions are loaded thrice: +# 1) To generate the build .exp files +# GDB_PERFTEST_MODE=gen-build-exps +# This step allows for parallel builds of the majority of pieces of the +# test binary and shlibs. +# 2) To compile the "pieces" of the binary and shlibs. +# "Pieces" are the bulk of the machine-generated sources of the test. +# This step is driven by lib/build-piece.exp. +# GDB_PERFTEST_MODE=build-pieces +# 3) To perform the final link of the binary and shlibs. +# GDB_PERFTEST_MODE=compile +# +# Example usage: +# bash$ make -j5 build-perf RUNTESTFLAGS="gmonster1.exp gmonster2.exp" +# bash$ make check-perf RUNTESTFLAGS="gdb.perf/gm*-*.exp GDB=/path/to/gdb" + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +if ![info exists MONSTER] { + set MONSTER "n" +} + +proc make_testcase_config { } { + global MONSTER + + set program_name "gmonster1" + array set testcase [GenPerfTest::init_testcase $program_name] + + set testcase(language) c++ + + # *_{sources,headers} need to be embedded in an outer list + # because remember each element of the outer list is for each run, and + # here we want to use the same value for all runs. + set testcase(binary_extra_sources) { { gmonster1.cc gm-hello.cc gm-use-cerr.cc gm-pervasive-typedef.cc } } + set testcase(binary_extra_headers) { { gm-utils.h gm-std.h gm-pervasive-typedef.h } } + set testcase(tail_shlib_sources) { { gm-std.cc } } + set testcase(tail_shlib_headers) { { gm-std.h } } + + if { $MONSTER == "y" } { + set testcase(run_names) { 10-cus 100-cus 1000-cus 10000-cus } + set testcase(nr_compunits) { 10 100 1000 10000 } + } else { + set testcase(run_names) { 1-cu 10-cus 100-cus } + set testcase(nr_compunits) { 1 10 100 } + } + set testcase(nr_gen_shlibs) { 0 } + + set testcase(nr_extern_functions) 10 + set testcase(nr_static_functions) 10 + + # class_specs needs to be embedded in an outer list because remember + # each element of the outer list is for each run, and here we want to use + # the same value for all runs. + set testcase(class_specs) { { { 0 10 } { 1 10 } { 2 10 } } } + set testcase(nr_members) 10 + set testcase(nr_static_members) 10 + set testcase(nr_methods) 10 + set testcase(nr_static_methods) 10 + + return [array get testcase] +} + +GenPerfTest::standard_compile_driver gmonster1.exp make_testcase_config diff --git a/gdb/testsuite/gdb.perf/gmonster2-backtrace.exp b/gdb/testsuite/gdb.perf/gmonster2-backtrace.exp new file mode 100644 index 0000000..a8073a5 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster2-backtrace.exp @@ -0,0 +1,26 @@ +# Copyright (C) 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 . + +# Measure performance of a backtrace in a large executable +# with lots of shared libraries. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster2.exp gmonster-backtrace.py make_testcase_config GmonsterBacktrace diff --git a/gdb/testsuite/gdb.perf/gmonster2-null-lookup.exp b/gdb/testsuite/gdb.perf/gmonster2-null-lookup.exp new file mode 100644 index 0000000..10c8e20 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster2-null-lookup.exp @@ -0,0 +1,26 @@ +# Copyright (C) 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 . + +# Measure speed of lookup of a symbol that doesn't exist +# with lots of shared libraries. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster2.exp make_testcase_config gmonster-null-lookup.py NullLookup diff --git a/gdb/testsuite/gdb.perf/gmonster2-pervasive-typedef.exp b/gdb/testsuite/gdb.perf/gmonster2-pervasive-typedef.exp new file mode 100644 index 0000000..cc5d428 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster2-pervasive-typedef.exp @@ -0,0 +1,27 @@ +# Copyright (C) 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 . + +# Measure the speed of "ptype func" where a parameter of the function is a +# typedef used pervasively. This exercises the perf regression introduced by +# the original patch to pr 16253. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster2.exp make_testcase_config gmonster-pervasive-typedef.py PervasiveTypedef diff --git a/gdb/testsuite/gdb.perf/gmonster2-print-cerr.exp b/gdb/testsuite/gdb.perf/gmonster2-print-cerr.exp new file mode 100644 index 0000000..a0c7d8b --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster2-print-cerr.exp @@ -0,0 +1,25 @@ +# Copyright (C) 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 . + +# Measure speed of printing std::cerr with lots of shared libraries. +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster2.exp make_testcase_config gmonster-print-cerr.py PrintCerr diff --git a/gdb/testsuite/gdb.perf/gmonster2-ptype-string.exp b/gdb/testsuite/gdb.perf/gmonster2-ptype-string.exp new file mode 100644 index 0000000..2c53435 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster2-ptype-string.exp @@ -0,0 +1,26 @@ +# Copyright (C) 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 . + +# Measure speed of ptype on a simple class in a library that is searched late +# with lots of shared libraries +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster2.exp make_testcase_config gmonster-ptype-string.py GmonsterPtypeString diff --git a/gdb/testsuite/gdb.perf/gmonster2-runto-main.exp b/gdb/testsuite/gdb.perf/gmonster2-runto-main.exp new file mode 100644 index 0000000..45a3d85 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster2-runto-main.exp @@ -0,0 +1,26 @@ +# Copyright (C) 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 . + +# Measure performance of selecting a file to debug and then running to main +# with lots of shared libraries +# Test parameters are the standard GenPerfTest parameters. + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +GenPerfTest::standard_run_driver gmonster2.exp make_testcase_config gmonster-runto-main.py GmonsterRuntoMain diff --git a/gdb/testsuite/gdb.perf/gmonster2.cc b/gdb/testsuite/gdb.perf/gmonster2.cc new file mode 100644 index 0000000..4ae9837 --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster2.cc @@ -0,0 +1,24 @@ +/* Copyright (C) 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 . */ + +#include "gm-std.h" + +int +main () +{ + gm_std::init (); + + return 0; +} diff --git a/gdb/testsuite/gdb.perf/gmonster2.exp b/gdb/testsuite/gdb.perf/gmonster2.exp new file mode 100644 index 0000000..db7d91c --- /dev/null +++ b/gdb/testsuite/gdb.perf/gmonster2.exp @@ -0,0 +1,88 @@ +# Copyright (C) 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 . + +# Perftest description file for building the "gmonster2" benchmark. +# Where does the name come from? The benchmark is derived from one of the +# monster programs at Google. +# +# Perftest descriptions are loaded thrice: +# 1) To generate the build .exp files +# GDB_PERFTEST_MODE=gen-build-exps +# This step allows for parallel builds of the majority of pieces of the +# test binary and shlibs. +# 2) To compile the "pieces" of the binary and shlibs. +# "Pieces" are the bulk of the machine-generated sources of the test. +# This step is driven by lib/build-piece.exp. +# GDB_PERFTEST_MODE=build-pieces +# 3) To perform the final link of the binary and shlibs. +# GDB_PERFTEST_MODE=compile +# +# Example usage: +# bash$ make -j5 build-perf RUNTESTFLAGS="gmonster1.exp gmonster2.exp" +# bash$ make check-perf RUNTESTFLAGS="gdb.perf/gm*-*.exp GDB=/path/to/gdb" + +load_lib perftest.exp + +if [skip_perf_tests] { + return 0 +} + +if ![info exists MONSTER] { + set MONSTER "n" +} + +proc make_testcase_config { } { + global MONSTER + + set program_name "gmonster2" + array set testcase [GenPerfTest::init_testcase $program_name] + + set testcase(language) c++ + + # *_{sources,headers} need to be embedded in an outer list + # because remember each element of the outer list is for each run, and + # here we want to use the same value for all runs. + set testcase(binary_extra_sources) { { gmonster2.cc gm-hello.cc gm-use-cerr.cc } } + set testcase(binary_extra_headers) { { gm-utils.h gm-std.h } } + set testcase(gen_shlib_extra_sources) { { gm-hello.cc gm-use-cerr.cc } } + set testcase(gen_shlib_extra_headers) { { gm-utils.h gm-std.h } } + set testcase(tail_shlib_sources) { { gm-std.cc } } + set testcase(tail_shlib_headers) { { gm-std.h } } + + if { $MONSTER == "y" } { + set testcase(run_names) { 10-sos 100-sos 1000-sos } + set testcase(nr_gen_shlibs) { 10 100 1000 } + } else { + set testcase(run_names) { 1-so 10-sos 100-sos } + set testcase(nr_gen_shlibs) { 1 10 100 } + } + set testcase(nr_compunits) 10 + + set testcase(nr_extern_functions) 10 + set testcase(nr_static_functions) 10 + + # class_specs needs to be embedded in an outer list because remember + # each element of the outer list is for each run, and here we want to use + # the same value for all runs. + set testcase(class_specs) { { { 0 10 } { 1 10 } { 2 10 } } } + set testcase(nr_members) 10 + set testcase(nr_static_members) 10 + set testcase(nr_methods) 10 + set testcase(nr_static_methods) 10 + + return [array get testcase] +} + +GenPerfTest::standard_compile_driver gmonster2.exp make_testcase_config diff --git a/gdb/testsuite/gdb.perf/lib/perftest/measure.py b/gdb/testsuite/gdb.perf/lib/perftest/measure.py index f0ecd48..fc767b6 100644 --- a/gdb/testsuite/gdb.perf/lib/perftest/measure.py +++ b/gdb/testsuite/gdb.perf/lib/perftest/measure.py @@ -103,6 +103,7 @@ class MeasurementCpuTime(Measurement): else: cpu_time = time.clock() - self.start_time self.result.record (id, cpu_time) + print ("elapsed cpu time %s" % (cpu_time)) class MeasurementWallTime(Measurement): """Measurement on Wall time.""" @@ -117,6 +118,7 @@ class MeasurementWallTime(Measurement): def stop(self, id): wall_time = time.time() - self.start_time self.result.record (id, wall_time) + print ("elapsed wall time %s" % (wall_time)) class MeasurementVmSize(Measurement): """Measurement on memory usage represented by VmSize.""" @@ -144,3 +146,4 @@ class MeasurementVmSize(Measurement): def stop(self, id): memory_used = self._compute_process_memory_usage("VmSize:") self.result.record (id, memory_used) + print ("vm used %s" % (memory_used)) diff --git a/gdb/testsuite/gdb.perf/lib/perftest/utils.py b/gdb/testsuite/gdb.perf/lib/perftest/utils.py new file mode 100644 index 0000000..66df8cf --- /dev/null +++ b/gdb/testsuite/gdb.perf/lib/perftest/utils.py @@ -0,0 +1,65 @@ +# Copyright (C) 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 . + +import gdb + +def safe_execute(command): + """Execute command, ignoring any gdb errors.""" + result = None + try: + result = gdb.execute(command, to_string=True) + except gdb.error: + pass + return result + + +def convert_spaces(file_name): + """Return file_name with all spaces replaced with "-".""" + return file_name.replace(" ", "-") + + +def select_file(file_name): + """Select a file for debugging. + + N.B. This turns confirmation off. + """ + safe_execute("set confirm off") + print ("Selecting file %s" % (file_name)) + gdb.execute("file %s" % (file_name)) + + +def runto(location): + """Run the program to location. + + N.B. This turns confirmation off. + """ + safe_execute("set confirm off") + gdb.execute("tbreak %s" % (location)) + gdb.execute("run") + + +def runto_main(): + """Run the program to "main". + + N.B. This turns confirmation off. + """ + runto("main") + + +def run_n_times(count, func): + """Execute func count times.""" + while count > 0: + func() + count -= 1 diff --git a/gdb/testsuite/gdb.perf/single-step.exp b/gdb/testsuite/gdb.perf/single-step.exp index 74c6de0..d5aa7e2 100644 --- a/gdb/testsuite/gdb.perf/single-step.exp +++ b/gdb/testsuite/gdb.perf/single-step.exp @@ -47,10 +47,12 @@ PerfTest::assemble { fail "Can't run to main" return -1 } + return 0 } { global SINGLE_STEP_COUNT gdb_test_no_output "python SingleStep\(${SINGLE_STEP_COUNT}\).run()" # Terminate the loop. gdb_test "set variable flag = 0" + return 0 } diff --git a/gdb/testsuite/gdb.perf/skip-prologue.exp b/gdb/testsuite/gdb.perf/skip-prologue.exp index 35db047..03d666b 100644 --- a/gdb/testsuite/gdb.perf/skip-prologue.exp +++ b/gdb/testsuite/gdb.perf/skip-prologue.exp @@ -52,6 +52,7 @@ PerfTest::assemble { fail "Can't run to main" return -1 } + return 0 } { global SKIP_PROLOGUE_COUNT @@ -66,4 +67,5 @@ PerfTest::assemble { pass $test } } + return 0 } diff --git a/gdb/testsuite/gdb.perf/solib.exp b/gdb/testsuite/gdb.perf/solib.exp index 4edc2ea..078a372 100644 --- a/gdb/testsuite/gdb.perf/solib.exp +++ b/gdb/testsuite/gdb.perf/solib.exp @@ -80,8 +80,10 @@ PerfTest::assemble { fail "Can't run to main" return -1 } + return 0 } { global SOLIB_COUNT gdb_test_no_output "python SolibLoadUnload\($SOLIB_COUNT\).run()" + return 0 } diff --git a/gdb/testsuite/lib/build-piece.exp b/gdb/testsuite/lib/build-piece.exp new file mode 100644 index 0000000..a81530c --- /dev/null +++ b/gdb/testsuite/lib/build-piece.exp @@ -0,0 +1,39 @@ +# Copyright (C) 2014 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 . + +# Utility to bootstrap building a piece of a performance test in a +# parallel build. +# See testsuite/Makefile.in:workers/%.worker. +# WORKER is set by the makefile and is +# "{program_name}/{program_name}-{worker_nr}". + +regexp "^\(.+\)/\(.+\)-\(\[0-9\]+\)$" $WORKER entire_match PROGRAM_NAME pname2 WORKER_NR + +if { ![info exists entire_match] || $entire_match != $WORKER } { + error "Bad value for WORKER: $WORKER" +} +if { $PROGRAM_NAME != $pname2 } { + error "Bad value for WORKER: $WORKER" +} + +# $subdir is set to "lib", because that is where this file lives, +# which is not what tests expect. +set subdir "gdb.perf" + +# $gdb_test_file_name is set to this file, build-piece, which is not what +# tests expect. +set gdb_test_file_name $PROGRAM_NAME + +source $srcdir/$subdir/${gdb_test_file_name}.exp diff --git a/gdb/testsuite/lib/cache.exp b/gdb/testsuite/lib/cache.exp index 8df04b9..9565b39 100644 --- a/gdb/testsuite/lib/cache.exp +++ b/gdb/testsuite/lib/cache.exp @@ -35,7 +35,7 @@ proc gdb_do_cache {name} { } if {[info exists GDB_PARALLEL]} { - set cache_filename [file join $objdir cache $cache_name] + set cache_filename [file join $objdir $GDB_PARALLEL cache $cache_name] if {[file exists $cache_filename]} { set fd [open $cache_filename] set gdb_data_cache($cache_name) [read -nonewline $fd] diff --git a/gdb/testsuite/lib/future.exp b/gdb/testsuite/lib/future.exp index 2fb635b..485da63 100644 --- a/gdb/testsuite/lib/future.exp +++ b/gdb/testsuite/lib/future.exp @@ -124,14 +124,15 @@ proc gdb_default_target_compile {source destfile type options} { error "Must supply an output filename for the compile to default_target_compile" } + set early_flags "" set add_flags "" set libs "" set compiler_type "c" set compiler "" set linker "" # linker_opts_order is one of "sources-then-flags", "flags-then-sources". - # The order shouldn't matter. It's done this way to preserve - # existing behavior. + # The order matters for things like -Wl,--as-needed. The default is to + # preserve existing behavior. set linker_opts_order "sources-then-flags" set ldflags "" set dest [target_info name] @@ -229,6 +230,10 @@ proc gdb_default_target_compile {source destfile type options} { regsub "^compiler=" $i "" tmp set compiler $tmp } + if {[regexp "^early_flags=" $i]} { + regsub "^early_flags=" $i "" tmp + append early_flags " $tmp" + } if {[regexp "^additional_flags=" $i]} { regsub "^additional_flags=" $i "" tmp append add_flags " $tmp" @@ -462,15 +467,15 @@ proc gdb_default_target_compile {source destfile type options} { # become confused about the name of the actual source file. switch $type { "object" { - set opts "$add_flags $sources" + set opts "$early_flags $add_flags $sources" } "executable" { switch $linker_opts_order { "flags-then-sources" { - set opts "$add_flags $sources" + set opts "$early_flags $add_flags $sources" } "sources-then-flags" { - set opts "$sources $add_flags" + set opts "$early_flags $sources $add_flags" } default { error "Invalid value for board_info linker_opts_order" @@ -478,7 +483,7 @@ proc gdb_default_target_compile {source destfile type options} { } } default { - set opts "$sources $add_flags" + set opts "$early_flags $sources $add_flags" } } diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 41797e7..33a8127 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -2728,12 +2728,20 @@ gdb_caching_proc target_is_gdbserver { return $is_gdbserver } -set compiler_info "unknown" +# N.B. compiler_info is intended to be local to this file. +# Call test_compiler_info with no arguments to fetch its value. +# Yes, this is counterintuitive when there's get_compiler_info, +# but that's the current API. +if [info exists compiler_info] { + unset compiler_info +} + set gcc_compiled 0 set hp_cc_compiler 0 set hp_aCC_compiler 0 # Figure out what compiler I am using. +# The result is cached so only the first invocation runs the compiler. # # ARG can be empty or "C++". If empty, "C" is assumed. # @@ -2800,6 +2808,11 @@ proc get_compiler_info {{arg ""}} { global hp_cc_compiler global hp_aCC_compiler + if [info exists compiler_info] { + # Already computed. + return 0 + } + # Choose which file to preprocess. set ifile "${srcdir}/lib/compiler.c" if { $arg == "c++" } { @@ -2841,8 +2854,14 @@ proc get_compiler_info {{arg ""}} { } } - # Reset to unknown compiler if any diagnostics happened. + # Set to unknown if for some reason compiler_info didn't get defined. + if ![info exists compiler_info] { + verbose -log "get_compiler_info: compiler_info not provided" + set compiler_info "unknown" + } + # Also set to unknown compiler if any diagnostics happened. if { $unknown } { + verbose -log "get_compiler_info: got unexpected diagnostics" set compiler_info "unknown" } @@ -2876,18 +2895,18 @@ proc get_compiler_info {{arg ""}} { return 0 } +# Return the compiler_info string if no arg is provided. +# Otherwise the argument is a glob-style expression to match against +# compiler_info. + proc test_compiler_info { {compiler ""} } { global compiler_info + get_compiler_info - # if no arg, return the compiler_info string - - if [string match "" $compiler] { - if [info exists compiler_info] { - return $compiler_info - } else { - perror "No compiler info found." - } - } + # If no arg, return the compiler_info string. + if [string match "" $compiler] { + return $compiler_info + } return [string match $compiler $compiler_info] } @@ -2966,6 +2985,13 @@ proc gdb_compile {source dest type options} { || [istarget *-*-cygwin*]) } { lappend new_options "additional_flags=-Wl,--enable-auto-import" } + if { [test_compiler_info "gcc-*"] || [test_compiler_info "clang-*"] } { + # Undo debian's change in the default. + # Put it at the front to not override any user-provided + # value, and to make sure it appears in front of all the + # shlibs! + lappend new_options "early_flags=-Wl,--no-as-needed" + } } } elseif { $opt == "shlib_load" } { set shlib_load 1 @@ -3894,7 +3920,7 @@ proc standard_output_file {basename} { global objdir subdir gdb_test_file_name GDB_PARALLEL if {[info exists GDB_PARALLEL]} { - set dir [file join $objdir outputs $subdir $gdb_test_file_name] + set dir [file join $objdir $GDB_PARALLEL outputs $subdir $gdb_test_file_name] file mkdir $dir return [file join $dir $basename] } else { @@ -3908,7 +3934,7 @@ proc standard_temp_file {basename} { global objdir GDB_PARALLEL if {[info exists GDB_PARALLEL]} { - return [file join $objdir temp $basename] + return [file join $objdir $GDB_PARALLEL temp $basename] } else { return $basename } @@ -4796,18 +4822,27 @@ proc build_executable { testname executable {sources ""} {options {debug}} } { return [eval build_executable_from_specs $arglist] } -# Starts fresh GDB binary and loads EXECUTABLE into GDB. EXECUTABLE is -# the basename of the binary. -# The return value is 0 for success, -1 for failure. -proc clean_restart { executable } { +# Starts fresh GDB binary and loads an optional executable into GDB. +# Usage: clean_restart [executable] +# EXECUTABLE is the basename of the binary. + +proc clean_restart { args } { global srcdir global subdir - set binfile [standard_output_file ${executable}] + + if { [llength $args] > 1 } { + error "bad number of args: [llength $args]" + } gdb_exit gdb_start gdb_reinitialize_dir $srcdir/$subdir - return [gdb_load ${binfile}] + + if { [llength $args] >= 1 } { + set executable [lindex $args 0] + set binfile [standard_output_file ${executable}] + gdb_load ${binfile} + } } # Prepares for testing by calling build_executable_full, then @@ -5011,7 +5046,10 @@ if {[info exists GDB_PARALLEL]} { if {[is_remote host]} { unset GDB_PARALLEL } else { - file mkdir outputs temp cache + file mkdir \ + [file join $GDB_PARALLEL outputs] \ + [file join $GDB_PARALLEL temp] \ + [file join $GDB_PARALLEL cache] } } diff --git a/gdb/testsuite/lib/perftest.exp b/gdb/testsuite/lib/perftest.exp index 7c334ac..88c92e4 100644 --- a/gdb/testsuite/lib/perftest.exp +++ b/gdb/testsuite/lib/perftest.exp @@ -12,6 +12,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# +# Notes: +# 1) This follows a Python convention for marking internal vs public functions. +# Internal functions are prefixed with "_". namespace eval PerfTest {