Patchwork add typedef printers to libstdc++

login
register
mail settings
Submitter Tom Tromey
Date Sept. 21, 2012, 8:59 p.m.
Message ID <87vcf7qegs.fsf@fleche.redhat.com>
Download mbox | patch
Permalink /patch/185930/
State New
Headers show

Comments

Tom Tromey - Sept. 21, 2012, 8:59 p.m.
This patch adds some typedef printers to libstdc++.

This relies on a gdb patch that hasn't yet gone in (pending on the list).
If the gdb patch changes, I'll change these printers as well.

The basic idea is that you can now have gdb substitute a name of your
choice when printing a type's name.  This lets a library pretend that
some typedefs are canonical.

This includes a fairly comprehensive test case for the new type
printers.

Tom

b/libstdc++-v3/ChangeLog:
2012-09-21  Tom Tromey  <tromey@redhat.com>

	* testsuite/libstdc++-prettyprinters/whatis.cc: New file.
	* testsuite/lib/gdb-test.exp (whatis-test): New proc.
	(gdb-test): Handle 'whatis' tests.
	(gdb_batch_check): New proc.
	(gdb_version_check): Rewrite to use gdb_batch_check.
	* python/libstdcxx/v6/printers.py: Import gdb.types.
	(FilteringTypePrinter): New class.
	(add_one_type_printer, register_type_printers): New functions.
	(register_libstdcxx_printers): Call register_type_printers.
Magnus Fromreide - Sept. 22, 2012, 8:54 a.m.
On Fri, 2012-09-21 at 14:59 -0600, Tom Tromey wrote:
> This patch adds some typedef printers to libstdc++.
> 
> This relies on a gdb patch that hasn't yet gone in (pending on the list).
> If the gdb patch changes, I'll change these printers as well.
> 
> The basic idea is that you can now have gdb substitute a name of your
> choice when printing a type's name.  This lets a library pretend that
> some typedefs are canonical.
> 
> This includes a fairly comprehensive test case for the new type
> printers.

I have not tested it, but when looking at the test case and also on the
code I couldn't help but ask myself how it handles other derivations.

How does it display the types of the variables us, s and ss in the
following code:
---
#include <string>

typedef std::basic_string<unsigned char> ustring;

ustring us;
std::string s;
std::basic_string<signed char> ss;
---

I would expect it to say std::basic_string<unsigned char,...>,
std::string and std::basic_string<signed char,...>, but I thought a test
case here couldn't hurt?

/MF

Patch

diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py
index 0eac413..5b11cb0 100644
--- a/libstdc++-v3/python/libstdcxx/v6/printers.py
+++ b/libstdc++-v3/python/libstdcxx/v6/printers.py
@@ -26,6 +26,15 @@  try:
 except ImportError:
     _use_gdb_pp = False
 
+# Try to install type-printers.
+_use_type_printing = False
+try:
+    import gdb.types
+    if hasattr(gdb.types, 'TypePrinter'):
+        _use_type_printing = True
+except ImportError:
+    pass
+
 # Starting with the type ORIG, search for the member type NAME.  This
 # handles searching upward through superclasses.  This is needed to
 # work around http://sourceware.org/bugzilla/show_bug.cgi?id=13615.
@@ -789,6 +798,97 @@  class Printer(object):
 
 libstdcxx_printer = None
 
+class FilteringTypePrinter(object):
+    def __init__(self, match, name):
+        self.match = match
+        self.name = name
+        self.enabled = True
+
+    class _recognizer(object):
+        def __init__(self, match, name):
+            self.match = match
+            self.name = name
+            self.type_obj = None
+
+        def recognize(self, type_obj):
+            if type_obj.tag is None:
+                return None
+
+            if self.type_obj is None:
+                if not self.match in type_obj.tag:
+                    # Filter didn't match.
+                    return None
+                try:
+                    self.type_obj = gdb.lookup_type(self.name).strip_typedefs()
+                except:
+                    pass
+            if self.type_obj == type_obj:
+                return self.name
+            return None
+
+    def instantiate(self):
+        return self._recognizer(self.match, self.name)
+
+def add_one_type_printer(obj, match, name):
+    printer = FilteringTypePrinter(match, 'std::' + name)
+    gdb.types.register_type_printer(obj, printer)
+
+def register_type_printers(obj):
+    global _use_type_printing
+
+    if not _use_type_printing:
+        return
+
+    for pfx in ('', 'w'):
+        add_one_type_printer(obj, 'basic_string', pfx + 'string')
+        add_one_type_printer(obj, 'basic_ios', pfx + 'ios')
+        add_one_type_printer(obj, 'basic_streambuf', pfx + 'streambuf')
+        add_one_type_printer(obj, 'basic_istream', pfx + 'istream')
+        add_one_type_printer(obj, 'basic_ostream', pfx + 'ostream')
+        add_one_type_printer(obj, 'basic_iostream', pfx + 'iostream')
+        add_one_type_printer(obj, 'basic_stringbuf', pfx + 'stringbuf')
+        add_one_type_printer(obj, 'basic_istringstream',
+                                 pfx + 'istringstream')
+        add_one_type_printer(obj, 'basic_ostringstream',
+                                 pfx + 'ostringstream')
+        add_one_type_printer(obj, 'basic_stringstream',
+                                 pfx + 'stringstream')
+        add_one_type_printer(obj, 'basic_filebuf', pfx + 'filebuf')
+        add_one_type_printer(obj, 'basic_ifstream', pfx + 'ifstream')
+        add_one_type_printer(obj, 'basic_ofstream', pfx + 'ofstream')
+        add_one_type_printer(obj, 'basic_fstream', pfx + 'fstream')
+        add_one_type_printer(obj, 'basic_regex', pfx + 'regex')
+        add_one_type_printer(obj, 'sub_match', pfx + 'csub_match')
+        add_one_type_printer(obj, 'sub_match', pfx + 'ssub_match')
+        add_one_type_printer(obj, 'match_results', pfx + 'cmatch')
+        add_one_type_printer(obj, 'match_results', pfx + 'smatch')
+        add_one_type_printer(obj, 'regex_iterator', pfx + 'cregex_iterator')
+        add_one_type_printer(obj, 'regex_iterator', pfx + 'sregex_iterator')
+        add_one_type_printer(obj, 'regex_token_iterator',
+                                 pfx + 'cregex_token_iterator')
+        add_one_type_printer(obj, 'regex_token_iterator',
+                                 pfx + 'sregex_token_iterator')
+
+    # Note that we can't have a printer for std::wstreampos, because
+    # it shares the same underlying type as std::streampos.
+    add_one_type_printer(obj, 'fpos', 'streampos')
+    add_one_type_printer(obj, 'basic_string', 'u16string')
+    add_one_type_printer(obj, 'basic_string', 'u32string')
+
+    for dur in ('nanoseconds', 'microseconds', 'milliseconds',
+                'seconds', 'minutes', 'hours'):
+        add_one_type_printer(obj, 'duration', dur)
+
+    add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand0')
+    add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand')
+    add_one_type_printer(obj, 'mersenne_twister_engine', 'mt19937')
+    add_one_type_printer(obj, 'mersenne_twister_engine', 'mt19937_64')
+    add_one_type_printer(obj, 'subtract_with_carry_engine', 'ranlux24_base')
+    add_one_type_printer(obj, 'subtract_with_carry_engine', 'ranlux48_base')
+    add_one_type_printer(obj, 'discard_block_engine', 'ranlux24')
+    add_one_type_printer(obj, 'discard_block_engine', 'ranlux48')
+    add_one_type_printer(obj, 'shuffle_order_engine', 'knuth_b')
+
 def register_libstdcxx_printers (obj):
     "Register libstdc++ pretty-printers with objfile Obj."
 
@@ -802,6 +902,8 @@  def register_libstdcxx_printers (obj):
             obj = gdb
         obj.pretty_printers.append(libstdcxx_printer)
 
+    register_type_printers(obj)
+
 def build_libstdcxx_dictionary ():
     global libstdcxx_printer
 
diff --git a/libstdc++-v3/testsuite/lib/gdb-test.exp b/libstdc++-v3/testsuite/lib/gdb-test.exp
index b9d4742..9457cfe 100644
--- a/libstdc++-v3/testsuite/lib/gdb-test.exp
+++ b/libstdc++-v3/testsuite/lib/gdb-test.exp
@@ -67,6 +67,13 @@  proc regexp-test {var result} {
     lappend gdb_tests $var $result 1
 }
 
+# A test of 'whatis'.  This tests a type rather than a variable.
+proc whatis-test {var result} {
+    global gdb_tests
+
+    lappend gdb_tests $var $result whatis
+}
+
 # Utility for testing variable values using gdb, invoked via dg-final.
 # Tests all tests indicated by note-test and regexp-test.
 #
@@ -84,6 +91,12 @@  proc gdb-test { marker {selector {}} } {
 	}
     }
 
+    set do_whatis_tests [gdb_batch_check "python print gdb.type_printers" \
+			   "\\\[\\\]"]
+    if {!$do_whatis_tests} {
+	send_log "skipping 'whatis' tests - gdb too old"
+    }
+
     # This assumes that we are three frames down from dg-test, and that
     # it still stores the filename of the testcase in a local variable "name".
     # A cleaner solution would require a new DejaGnu release.
@@ -109,12 +122,21 @@  proc gdb-test { marker {selector {}} } {
     puts $fd "run"
 
     set count 0
-    foreach {var result is_regexp} $gdb_tests {
-	puts $fd "print $var"
+    foreach {var result kind} $gdb_tests {
 	incr count
 	set gdb_var($count) $var
 	set gdb_expected($count) $result
-	set gdb_is_regexp($count) $is_regexp
+	if {$kind == "whatis"} {
+	    if {$do_whatis_tests} {
+		set gdb_is_type($count) 1
+		set gdb_command($count) "whatis $var"
+	    }
+	} else {
+	    set gdb_is_type($count) 0
+	    set gdb_is_regexp($count) $kind
+	    set gdb_command($count) "print $var"
+	}
+	puts $fd $gdb_command($count)
     }
     set gdb_tests {}
 
@@ -128,28 +150,36 @@  proc gdb-test { marker {selector {}} } {
 	return
     }
 
+    set test_counter 0
     remote_expect target [timeout_value] {
-	-re {^\$([0-9]+) = ([^\n\r]*)[\n\r]+} {
+	-re {^(type|\$([0-9]+)) = ([^\n\r]*)[\n\r]+} {
 	    send_log "got: $expect_out(buffer)"
 
-	    set num $expect_out(1,string)
-	    set first $expect_out(2,string)
-
-	    if {$gdb_is_regexp($num)} {
-		set match [regexp -- $gdb_expected($num) $first]
+	    incr test_counter
+	    set first $expect_out(3,string)
+
+	    if {$gdb_is_type($test_counter)} {
+		if {$expect_out(1,string) != "type"} {
+		    error "gdb failure"
+		}
+		set match [expr {![string compare $first \
+				     $gdb_expected($test_counter)]}]
+	    } elseif {$gdb_is_regexp($test_counter)} {
+		set match [regexp -- $gdb_expected($test_counter) $first]
 	    } else {
-		set match [expr {![string compare $first $gdb_expected($num)]}]
+		set match [expr {![string compare $first \
+				     $gdb_expected($test_counter)]}]
 	    }
 
 	    if {$match} {
-		pass "$testname print $gdb_var($num)"
+		pass "$testname $gdb_command($test_counter)"
 	    } else {
-		fail "$testname print $gdb_var($num)"
+		fail "$testname $gdb_command($test_counter)"
 		verbose "     got =>$first<="
-		verbose "expected =>$gdb_expected($num)<="
+		verbose "expected =>$gdb_expected($test_counter)<="
 	    }
 
-	    if {$num == $count} {
+	    if {$test_counter == $count} {
 		remote_close target
 		return
 	    } else {
@@ -180,16 +210,10 @@  proc gdb-test { marker {selector {}} } {
     return
 }
 
-# Check for a new-enough version of gdb.  The pretty-printer tests
-# require gdb 7.3, but we don't want to test versions, so instead we
-# check for the python "lookup_global_symbol" method, which is in 7.3
-# but not earlier versions.
-# Return 1 if the version is ok, 0 otherwise.
-proc gdb_version_check {} {
-    global gdb_version
-
+# Invoke gdb with a command and pattern-match the output.
+proc gdb_batch_check {command pattern} {
     set gdb_name $::env(GUALITY_GDB_NAME)
-    set cmd "$gdb_name -nw -nx -quiet -batch -ex \"python print gdb.lookup_global_symbol\""
+    set cmd "$gdb_name -nw -nx -quiet -batch -ex \"$command\""
     send_log "Spawning: $cmd\n"
     set res [remote_spawn target "$cmd"]
     if { $res < 0 || $res == "" } {
@@ -197,7 +221,7 @@  proc gdb_version_check {} {
     }
 
     remote_expect target [timeout_value] {
-	-re "<built-in function lookup_global_symbol>" {
+	-re $pattern {
 	    return 1
 	}
 
@@ -215,3 +239,13 @@  proc gdb_version_check {} {
     remote_close target
     return 0
 }
+
+# Check for a new-enough version of gdb.  The pretty-printer tests
+# require gdb 7.3, but we don't want to test versions, so instead we
+# check for the python "lookup_global_symbol" method, which is in 7.3
+# but not earlier versions.
+# Return 1 if the version is ok, 0 otherwise.
+proc gdb_version_check {} {
+    return [gdb_batch_check "python print gdb.lookup_global_symbol" \
+	      "<built-in function lookup_global_symbol>"]
+}
diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc
new file mode 100644
index 0000000..259694d
--- /dev/null
+++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc
@@ -0,0 +1,229 @@ 
+// { dg-do run }
+// { dg-options "-g -O0 -std=gnu++11" }
+
+// Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library 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, or (at your option)
+// any later version.
+
+// This library 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 library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <string>
+#include <iostream>
+#include <regex>
+
+template<class T>
+void
+placeholder(const T *s)
+{
+  std::cout << (void *) s;
+}
+
+template<class T>
+struct holder
+{
+  T *f;
+};
+
+// This test is written in a somewhat funny way.
+// Each type under test is used twice: first, to form a pointer type,
+// and second, as a template parameter.  This is done to work around
+// apparent GCC oddities.  The pointer type is needed to ensure that
+// the typedef in question ends up in the debuginfo; while the
+// template type is used to ensure that a typedef-less variant is
+// presented to gdb.
+
+std::string *string_ptr;
+holder<std::string> string_holder;
+// { dg-final { whatis-test string_holder "holder<std::string>" } }
+std::ios *ios_ptr;
+holder<std::ios> ios_holder;
+// { dg-final { whatis-test ios_holder "holder<std::ios>" } }
+std::streambuf *streambuf_ptr;
+holder<std::streambuf> streambuf_holder;
+// { dg-final { whatis-test streambuf_holder "holder<std::streambuf>" } }
+std::istream *istream_ptr;
+holder<std::istream> istream_holder;
+// { dg-final { whatis-test istream_holder "holder<std::istream>" } }
+std::ostream *ostream_ptr;
+holder<std::ostream> ostream_holder;
+// { dg-final { whatis-test ostream_holder "holder<std::ostream>" } }
+std::iostream *iostream_ptr;
+holder<std::iostream> iostream_holder;
+// { dg-final { whatis-test iostream_holder "holder<std::iostream>" } }
+std::stringbuf *stringbuf_ptr;
+holder<std::stringbuf> stringbuf_holder;
+// { dg-final { whatis-test stringbuf_holder "holder<std::stringbuf>" } }
+std::istringstream *istringstream_ptr;
+holder<std::istringstream> istringstream_holder;
+// { dg-final { whatis-test istringstream_holder "holder<std::istringstream>" } }
+std::ostringstream *ostringstream_ptr;
+holder<std::ostringstream> ostringstream_holder;
+// { dg-final { whatis-test ostringstream_holder "holder<std::ostringstream>" } }
+std::stringstream *stringstream_ptr;
+holder<std::stringstream> stringstream_holder;
+// { dg-final { whatis-test stringstream_holder "holder<std::stringstream>" } }
+std::filebuf *filebuf_ptr;
+holder<std::filebuf> filebuf_holder;
+// { dg-final { whatis-test filebuf_holder "holder<std::filebuf>" } }
+std::ifstream *ifstream_ptr;
+holder<std::ifstream> ifstream_holder;
+// { dg-final { whatis-test ifstream_holder "holder<std::ifstream>" } }
+std::ofstream *ofstream_ptr;
+holder<std::ofstream> ofstream_holder;
+// { dg-final { whatis-test ofstream_holder "holder<std::ofstream>" } }
+std::fstream *fstream_ptr;
+holder<std::fstream> fstream_holder;
+// { dg-final { whatis-test fstream_holder "holder<std::fstream>" } }
+std::streampos *streampos_ptr;
+holder<std::streampos> streampos_holder;
+// { dg-final { whatis-test streampos_holder "holder<std::streampos>" } }
+std::regex *regex_ptr;
+holder<std::regex> regex_holder;
+// { dg-final { whatis-test regex_holder "holder<std::regex>" } }
+std::csub_match *csub_match_ptr;
+holder<std::csub_match> csub_match_holder;
+// { dg-final { whatis-test csub_match_holder "holder<std::csub_match>" } }
+std::ssub_match *ssub_match_ptr;
+holder<std::ssub_match> ssub_match_holder;
+// { dg-final { whatis-test ssub_match_holder "holder<std::ssub_match>" } }
+std::cmatch *cmatch_ptr;
+holder<std::cmatch> cmatch_holder;
+// { dg-final { whatis-test cmatch_holder "holder<std::cmatch>" } }
+std::smatch *smatch_ptr;
+holder<std::smatch> smatch_holder;
+// { dg-final { whatis-test smatch_holder "holder<std::smatch>" } }
+std::cregex_iterator *cregex_iterator_ptr;
+holder<std::cregex_iterator> cregex_iterator_holder;
+// { dg-final { whatis-test cregex_iterator_holder "holder<std::cregex_iterator>" } }
+std::sregex_iterator *sregex_iterator_ptr;
+holder<std::sregex_iterator> sregex_iterator_holder;
+// { dg-final { whatis-test sregex_iterator_holder "holder<std::sregex_iterator>" } }
+std::cregex_token_iterator *cregex_token_iterator_ptr;
+holder<std::cregex_token_iterator> cregex_token_iterator_holder;
+// { dg-final { whatis-test cregex_token_iterator_holder "holder<std::cregex_token_iterator>" } }
+std::sregex_token_iterator *sregex_token_iterator_ptr;
+holder<std::sregex_token_iterator> sregex_token_iterator_holder;
+// { dg-final { whatis-test sregex_token_iterator_holder "holder<std::sregex_token_iterator>" } }
+std::u16string *u16string_ptr;
+holder<std::u16string> u16string_holder;
+// { dg-final { whatis-test u16string_holder "holder<std::u16string>" } }
+std::u32string *u32string_ptr;
+holder<std::u32string> u32string_holder;
+// { dg-final { whatis-test u32string_holder "holder<std::u32string>" } }
+std::minstd_rand0 *minstd_rand0_ptr;
+holder<std::minstd_rand0> minstd_rand0_holder;
+// { dg-final { whatis-test minstd_rand0_holder "holder<std::minstd_rand0>" } }
+std::minstd_rand *minstd_rand_ptr;
+holder<std::minstd_rand> minstd_rand_holder;
+// { dg-final { whatis-test minstd_rand_holder "holder<std::minstd_rand>" } }
+std::mt19937 *mt19937_ptr;
+holder<std::mt19937> mt19937_holder;
+// { dg-final { whatis-test mt19937_holder "holder<std::mt19937>" } }
+std::mt19937_64 *mt19937_64_ptr;
+holder<std::mt19937_64> mt19937_64_holder;
+// { dg-final { whatis-test mt19937_64_holder "holder<std::mt19937_64>" } }
+std::ranlux24_base *ranlux24_base_ptr;
+holder<std::ranlux24_base> ranlux24_base_holder;
+// { dg-final { whatis-test ranlux24_base_holder "holder<std::ranlux24_base>" } }
+std::ranlux48_base *ranlux48_base_ptr;
+holder<std::ranlux48_base> ranlux48_base_holder;
+// { dg-final { whatis-test ranlux48_base_holder "holder<std::ranlux48_base>" } }
+std::ranlux24 *ranlux24_ptr;
+holder<std::ranlux24> ranlux24_holder;
+// { dg-final { whatis-test ranlux24_holder "holder<std::ranlux24>" } }
+std::ranlux48 *ranlux48_ptr;
+holder<std::ranlux48> ranlux48_holder;
+// { dg-final { whatis-test ranlux48_holder "holder<std::ranlux48>" } }
+std::knuth_b *knuth_b_ptr;
+holder<std::knuth_b> knuth_b_holder;
+// { dg-final { whatis-test knuth_b_holder "holder<std::knuth_b>" } }
+
+int
+main()
+{
+  placeholder(&ios_ptr);		// Mark SPOT
+  placeholder(&ios_holder);
+  placeholder(&string_ptr);
+  placeholder(&string_holder);
+  placeholder(&streambuf_ptr);
+  placeholder(&streambuf_holder);
+  placeholder(&istream_ptr);
+  placeholder(&istream_holder);
+  placeholder(&ostream_ptr);
+  placeholder(&ostream_holder);
+  placeholder(&iostream_ptr);
+  placeholder(&iostream_holder);
+  placeholder(&stringbuf_ptr);
+  placeholder(&stringbuf_holder);
+  placeholder(&istringstream_ptr);
+  placeholder(&istringstream_holder);
+  placeholder(&ostringstream_ptr);
+  placeholder(&ostringstream_holder);
+  placeholder(&stringstream_ptr);
+  placeholder(&stringstream_holder);
+  placeholder(&filebuf_ptr);
+  placeholder(&filebuf_holder);
+  placeholder(&ifstream_ptr);
+  placeholder(&ifstream_holder);
+  placeholder(&ofstream_ptr);
+  placeholder(&ofstream_holder);
+  placeholder(&fstream_ptr);
+  placeholder(&fstream_holder);
+  placeholder(&streampos_ptr);
+  placeholder(&streampos_holder);
+  placeholder(&regex_ptr);
+  placeholder(&regex_holder);
+  placeholder(&csub_match_ptr);
+  placeholder(&csub_match_holder);
+  placeholder(&ssub_match_ptr);
+  placeholder(&ssub_match_holder);
+  placeholder(&cmatch_ptr);
+  placeholder(&cmatch_holder);
+  placeholder(&smatch_ptr);
+  placeholder(&smatch_holder);
+  placeholder(&cregex_iterator_ptr);
+  placeholder(&cregex_iterator_holder);
+  placeholder(&sregex_iterator_ptr);
+  placeholder(&sregex_iterator_holder);
+  placeholder(&cregex_token_iterator_ptr);
+  placeholder(&cregex_token_iterator_holder);
+  placeholder(&sregex_token_iterator_ptr);
+  placeholder(&sregex_token_iterator_holder);
+  placeholder(&u16string_ptr);
+  placeholder(&u16string_holder);
+  placeholder(&u32string_ptr);
+  placeholder(&u32string_holder);
+  placeholder(&minstd_rand0_ptr);
+  placeholder(&minstd_rand0_holder);
+  placeholder(&minstd_rand_ptr);
+  placeholder(&minstd_rand_holder);
+  placeholder(&mt19937_ptr);
+  placeholder(&mt19937_holder);
+  placeholder(&mt19937_64_ptr);
+  placeholder(&mt19937_64_holder);
+  placeholder(&ranlux24_base_ptr);
+  placeholder(&ranlux24_base_holder);
+  placeholder(&ranlux48_base_ptr);
+  placeholder(&ranlux48_base_holder);
+  placeholder(&ranlux24_ptr);
+  placeholder(&ranlux24_holder);
+  placeholder(&ranlux48_ptr);
+  placeholder(&ranlux48_holder);
+  placeholder(&knuth_b_ptr);
+  placeholder(&knuth_b_holder);
+
+  return 0;
+}
+
+// { dg-final { gdb-test SPOT } }