| 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 keys and values. | |
| Supported keys are: | |
| count: number of classes | |
| Default: 1 | |
| name: list of namespaces and class name prefix | |
| E.g., { ns0 ns1 foo } -> ns0::ns1::foo_<cu#>_{0,1,...} | |
| There is no default, this value must be specified. | |
| nr_members: number of members | |
| Default: 0 | |
| nr_static_members: number of static members | |
| Default: 0 | |
| nr_methods: number of methods | |
| Default: 0 | |
| nr_inline_methods: number of inline methods | |
| Default: 0 | |
| nr_static_methods: number of static methods | |
| Default: 0 | |
| nr_static_inline_methods: number of static inline methods | |
| Default: 0 | |
| E.g., | |
| class foo {}; | |
| namespace ns1 { class bar {}; } | |
| would be represented as: | |
| { | |
| { count 1 name { foo } } | |
| { count 1 name { ns1 bar } } | |
| } | |
| The naming of each class is "class_<cu_nr>_<class_nr>", | |
| where <cu_nr> is the number of the compilation unit the | |
| class is defined in. | |
| There's currently no support for nesting classes in classes, | |
| or for specifying baseclasses or templates. | |
| 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 |