[v2] Add pretty printers for the NPTL lock types

Message ID 1444429496-3840-1-git-send-email-martin.galvan@tallertechnologies.com
State Superseded
Headers

Commit Message

Martin Galvan Oct. 9, 2015, 10:24 p.m. UTC
  This patch adds the pretty-printers for the following NPTL types:

- pthread_mutex_t
- pthread_mutexattr_t
- pthread_cond_t
- pthread_condattr_t
- pthread_rwlock_t
- pthread_rwlockattr_t

To load the pretty-printers into your gdb session, do the following:

python
import sys
sys.path.insert(0, '/path/to/glibc/install/bin/')
end
source /path/to/glibc/install/bin/nptl-printers.py

You can check which printers are registered and enabled by issuing the
'info pretty-printer' gdb command. Printers should trigger automatically when
trying to print a variable of one of the types mentioned above.

The printers are architecture-independent, and were tested on both the gdb CLI
and Eclipse CDT.

In order to work, the printers need to know the values of various flags that
are scattered throughout pthread.h and pthreadP.h as enums and #defines. Since
replicating these constants in the printers file itself would create a
maintenance burden, I followed Joseph Myers' advice and wrote a script
called gen-py-const.awk that Makerules uses to extract the constants.
This script is pretty much the same as gen-as-const.awk, except it doesn't cast
the constant values to 'long' and is thorougly documented. The constants need
only to be enumerated in a .pysym file, which is then referenced by a Make
variable called gen-py-const-headers.

As for the Make targets, I wrote them so that the files would all end up
in install/bin. I admit that this may not be the best place to install them,
but the glibc build/install process is so convoluted that it'd take me forever
to do something better. Any improvements on the Makefiles should be handled by
someone who has a more intimate knowledge of the build process and GNU Make
in general.

I have a company-wide copyright assignment for glibc. I don't have write access,
though, so if anyone could commit this for me it would be great.

---
 ChangeLog                      |  10 +
 Makerules                      |  28 ++
 nptl/Makefile                  |  11 +
 nptl/nptl-printers.py          | 671 +++++++++++++++++++++++++++++++++++++++++
 nptl/nptl_lock_constants.pysym |  66 ++++
 scripts/gen-py-const.awk       | 116 +++++++
 6 files changed, 902 insertions(+)
 create mode 100644 nptl/nptl-printers.py
 create mode 100644 nptl/nptl_lock_constants.pysym
 create mode 100644 scripts/gen-py-const.awk

--
2.6.1
  

Comments

Mike Frysinger Oct. 19, 2015, 11:03 p.m. UTC | #1
On 09 Oct 2015 19:24, Martin Galvan wrote:
> --- a/Makerules
> +++ b/Makerules
> 
> +ifdef gen-py-const-headers
> +# This is a hack we use to generate .py files with constants for Python
> +# pretty-printers. It works the same way as gen-as-const. See gen-py-const
> +# for details on how the awk | gcc mechanism works.
> +$(common-objpfx)%.py: $(..)scripts/gen-py-const.awk %.pysym $(common-before-compile)
> +	$(AWK) -f $< $(filter %.pysym,$^)

this duplicated line left in by accident for debugging ?  should delete it.

> +	echo '# GENERATED FILE, DO NOT EDIT\n' > $@
> +	echo '# Constant definitions for the NPTL pretty printers.' >> $@
> +	echo '# See gen-py-const.awk for details.\n' >> $@
> +
> +	# Replace "@name@SOME_NAME@value@SOME_VALUE@" strings from the output of
> +	# gen-py-const.awk with "SOME_NAME = SOME_VALUE" strings.
> +	# The '-n' option, combined with the '/p' command, makes sed output only the
> +	# modified lines instead of the whole input file. The output is redirected
> +	# to a .py file; we'll import it from printers.py to read the constants
> +	# generated by gen-py-const.awk.
> +	# The regex has two capturing groups, for SOME_NAME and SOME_VALUE
> +	# respectively. Notice SOME_VALUE will start with a '$'; Make requires that
> +	# we write '$' twice.

these comments are at the shell level instead of makelevel which means they get
printed to stdout.  would be best to change them to make comments (rather than
just adding a @ prefix) so we don't waste time execing a shell to do nothing.

> +	sed -n 's/^.*@name@\([^@]\+\)@value@\$$\([0-9Xxa-fA-F-]\+\)@.*/\1 = \2/p' \

imo we should use -E rather than doing \( \+ \) stuff so we get POSIX ERE 
behavior by default

also, the use of a-f and such are dangerous.  use [:hexdigit:] instead like:
	[[:hexdigit:]Xx-]

> +# Install the Python pretty-printers.
> +py-const = $(patsubst %.pysym,%.py,$(gen-py-const-headers))
> +install-bin-script = $(py-const) nptl-printers.py

why is this install-bin-script ?  we don't want to install these into /usr/bin/.
gdb pretty printers usually go into /usr/share/gdb/auto-load/<fspath>.  since
these are for the pthreads lib, you'd want like:
	/usr/share/gdb/auto-load/lib/libpthread.so.0
obviously the path & name will vary based on the install of libpthread.  we
probably also want to add a configure flag for the gdb autoload path as i
vaguely recall different distros using different base paths.

> +++ b/nptl/nptl-printers.py

at a high level, you should run this through pylint

> +'info pretty-printer' gdb command. Printers should trigger automatically when

GNU style puts two spaces after the period

> """

please put before all imports:
	from __future__ import print_function

> +import sys
> +import re
> +import ctypes
> +import gdb

system modules should be sorted, then a blank line, then the gdb imports

although ctypes looks unused so you should drop it:
W: 37, 0: Unused import ctypes (unused-import)


> +class _IteratorP3(object):
> +    """A simple Iterator class."""

this docstring needs to explain what they're for.  glancing at their use, i
don't see what they're good for.  if you need a real iterator, you could just
use itertools.chain() and delete these two classes entirely.

> +class MutexPrinter(object):

glancing at the diff classes, they seem to have  ommon behavior.  maybe create
a local class and implement the common behavior there ?  at least the children
func looks the same.

> +################################################################################

i don't see much value in these lines.  just punt them.

> +        self.values.append(('Threads waiting for this condvar',
> +                           self.nwaiters >> COND_NWAITERS_SHIFT))

trailing indent is wrong:
C:376, 0: Wrong continued indentation.
                           self.nwaiters >> COND_NWAITERS_SHIFT))
                           ^| (bad-continuation)

> +    def to_string(self):
> +        """gdb API function.
> +
> +        This is called from gdb when we try to print a pthread_rwlockattr_t."""
> ...
> +    def children(self):
> +        """gdb API function.
> +
> +        This is called from gdb when we try to print a pthread_rwlockattr_t."""

both of these docstrings shouldn't be cuddled:
C:557, 4: Closing triple quotes should not be cuddled (docstring-cuddled-quotes)
C:557, 4: Trailing whitespace in docstring: offset:1: {} (docstring-trailing-whitespace)
C:564, 4: Closing triple quotes should not be cuddled (docstring-cuddled-quotes)
C:564, 4: Trailing whitespace in docstring: offset:1: {} (docstring-trailing-whitespace)

> +        def __init__(self, name, regex, callable):

is |callable| part of the gdb API ?  if not, you should rename it:
W:610,40: Redefining built-in 'callable' (redefined-builtin)

> +            """
> +            Initialize a pretty-printer.

this should be on the first line:
C:610, 8: First line should be a short summary (docstring-first-line)

> +    def addSubprinter(self, name, regex, callable):

same here wrt |callable|

> +    printer.addSubprinter('pthread_rwlock_t', '^pthread_rwlock_t$', RWLockPrinter)

line is too long:
C:665, 0: Line too long (82/80) (line-too-long)
-mike
  
Martin Galvan Oct. 20, 2015, 12:28 p.m. UTC | #2
Hi Mike! Thanks for the feedback.

On Mon, Oct 19, 2015 at 8:03 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> On 09 Oct 2015 19:24, Martin Galvan wrote:
>> --- a/Makerules
>> +++ b/Makerules
>>
>> +ifdef gen-py-const-headers
>> +# This is a hack we use to generate .py files with constants for Python
>> +# pretty-printers. It works the same way as gen-as-const. See gen-py-const
>> +# for details on how the awk | gcc mechanism works.
>> +$(common-objpfx)%.py: $(..)scripts/gen-py-const.awk %.pysym $(common-before-compile)
>> +     $(AWK) -f $< $(filter %.pysym,$^)
>
> this duplicated line left in by accident for debugging ?  should delete it.

Indeed, it was there for debugging :) will remove it in v3.

>> +     echo '# GENERATED FILE, DO NOT EDIT\n' > $@
>> +     echo '# Constant definitions for the NPTL pretty printers.' >> $@
>> +     echo '# See gen-py-const.awk for details.\n' >> $@
>> +
>> +     # Replace "@name@SOME_NAME@value@SOME_VALUE@" strings from the output of
>> +     # gen-py-const.awk with "SOME_NAME = SOME_VALUE" strings.
>> +     # The '-n' option, combined with the '/p' command, makes sed output only the
>> +     # modified lines instead of the whole input file. The output is redirected
>> +     # to a .py file; we'll import it from printers.py to read the constants
>> +     # generated by gen-py-const.awk.
>> +     # The regex has two capturing groups, for SOME_NAME and SOME_VALUE
>> +     # respectively. Notice SOME_VALUE will start with a '$'; Make requires that
>> +     # we write '$' twice.
>
> these comments are at the shell level instead of makelevel which means they get
> printed to stdout.  would be best to change them to make comments (rather than
> just adding a @ prefix) so we don't waste time execing a shell to do nothing.

Raelly? While I did see them in the make output, I thought anything
that comes after a '#' is a regula Make comment.

>> +     sed -n 's/^.*@name@\([^@]\+\)@value@\$$\([0-9Xxa-fA-F-]\+\)@.*/\1 = \2/p' \
>
> imo we should use -E rather than doing \( \+ \) stuff so we get POSIX ERE
> behavior by default

Is -E a GNU sed flag? I'm not seeing it on the man page, though -r
seems to do the same thing.

> also, the use of a-f and such are dangerous.  use [:hexdigit:] instead like:
>         [[:hexdigit:]Xx-]

Will do. Perhaps the sed line used for gen-as-const should be changed
as well, since that's where I got it from. I'll change that one in a
future patch.

>> +# Install the Python pretty-printers.
>> +py-const = $(patsubst %.pysym,%.py,$(gen-py-const-headers))
>> +install-bin-script = $(py-const) nptl-printers.py
>
> why is this install-bin-script ?  we don't want to install these into /usr/bin/.
> gdb pretty printers usually go into /usr/share/gdb/auto-load/<fspath>.  since
> these are for the pthreads lib, you'd want like:
>         /usr/share/gdb/auto-load/lib/libpthread.so.0
> obviously the path & name will vary based on the install of libpthread.  we
> probably also want to add a configure flag for the gdb autoload path as i
> vaguely recall different distros using different base paths.

That'd be great. However, this would be best addressed by someone
who's more familiar with the glibc build process, and Make and
autoconf in general. I don't know how to fix this myself.

>> +++ b/nptl/nptl-printers.py
>
> at a high level, you should run this through pylint

I remember running pylint on this, but admittedly it was a long time ago :)

>> +'info pretty-printer' gdb command. Printers should trigger automatically when
>
> GNU style puts two spaces after the period

Ok.

> please put before all imports:
>         from __future__ import print_function

Ok.

>> +import sys
>> +import re
>> +import ctypes
>> +import gdb
>
> system modules should be sorted, then a blank line, then the gdb imports
>
> although ctypes looks unused so you should drop it:
> W: 37, 0: Unused import ctypes (unused-import)

This seems to be a remnant from a previous version of the printers.
Will drop it.

>> +class _IteratorP3(object):
>> +    """A simple Iterator class."""
>
> this docstring needs to explain what they're for.  glancing at their use, i
> don't see what they're good for.  if you need a real iterator, you could just
> use itertools.chain() and delete these two classes entirely.

Will look into this, although I recall writing these classes because I
saw something similar being used in the libstdc++ printers.

>> +class MutexPrinter(object):
>
> glancing at the diff classes, they seem to have  ommon behavior.  maybe create
> a local class and implement the common behavior there ?  at least the children
> func looks the same.

Will look into it.

>> +################################################################################
>
> i don't see much value in these lines.  just punt them.

Ok.

>> +        self.values.append(('Threads waiting for this condvar',
>> +                           self.nwaiters >> COND_NWAITERS_SHIFT))
>
> trailing indent is wrong:
> C:376, 0: Wrong continued indentation.
>                            self.nwaiters >> COND_NWAITERS_SHIFT))
>                            ^| (bad-continuation)

Did pylint report that? If so, how did you configure it? I'm asking
this so I can fix any other errors that may pop out.

>> +    def to_string(self):
>> +        """gdb API function.
>> +
>> +        This is called from gdb when we try to print a pthread_rwlockattr_t."""
>> ...
>> +    def children(self):
>> +        """gdb API function.
>> +
>> +        This is called from gdb when we try to print a pthread_rwlockattr_t."""
>
> both of these docstrings shouldn't be cuddled:
> C:557, 4: Closing triple quotes should not be cuddled (docstring-cuddled-quotes)
> C:557, 4: Trailing whitespace in docstring: offset:1: {} (docstring-trailing-whitespace)
> C:564, 4: Closing triple quotes should not be cuddled (docstring-cuddled-quotes)
> C:564, 4: Trailing whitespace in docstring: offset:1: {} (docstring-trailing-whitespace)
>
>> +        def __init__(self, name, regex, callable):
>
> is |callable| part of the gdb API ?  if not, you should rename it:
> W:610,40: Redefining built-in 'callable' (redefined-builtin)

Not really. Will rename it.

>> +            """
>> +            Initialize a pretty-printer.
>
> this should be on the first line:
> C:610, 8: First line should be a short summary (docstring-first-line)
>
>> +    def addSubprinter(self, name, regex, callable):
>
> same here wrt |callable|
>
>> +    printer.addSubprinter('pthread_rwlock_t', '^pthread_rwlock_t$', RWLockPrinter)
>
> line is too long:
> C:665, 0: Line too long (82/80) (line-too-long)
> -mike

Ok.

I'll be sending v3 after fixing these findings. It would be great if
anyone else wants to review this in the meanwhile. Thanks a lot!
  
Mike Frysinger Oct. 20, 2015, 1:43 p.m. UTC | #3
On 20 Oct 2015 09:28, Martin Galvan wrote:
> On Mon, Oct 19, 2015 at 8:03 PM, Mike Frysinger wrote:
> > On 09 Oct 2015 19:24, Martin Galvan wrote:
> >> +     # Replace "@name@SOME_NAME@value@SOME_VALUE@" strings from the output of
> >> +     # gen-py-const.awk with "SOME_NAME = SOME_VALUE" strings.
> >> +     # The '-n' option, combined with the '/p' command, makes sed output only the
> >> +     # modified lines instead of the whole input file. The output is redirected
> >> +     # to a .py file; we'll import it from printers.py to read the constants
> >> +     # generated by gen-py-const.awk.
> >> +     # The regex has two capturing groups, for SOME_NAME and SOME_VALUE
> >> +     # respectively. Notice SOME_VALUE will start with a '$'; Make requires that
> >> +     # we write '$' twice.
> >
> > these comments are at the shell level instead of makelevel which means they get
> > printed to stdout.  would be best to change them to make comments (rather than
> > just adding a @ prefix) so we don't waste time execing a shell to do nothing.
> 
> Raelly? While I did see them in the make output, I thought anything
> that comes after a '#' is a regula Make comment.

negative.  you can run strace to see:
$ cat Makefile 
all:
	@echo hi
	@# comment
$ strace -f -eexecve make
execve("/usr/bin/make", ["make"], [/* 74 vars */]) = 0
[pid 29566] execve("/bin/echo", ["echo", "hi"], [/* 79 vars */]) = 0
[pid 29567] execve("/bin/sh", ["/bin/sh", "-c", "# comment"], [/* 79 vars */]) = 0

> >> +     sed -n 's/^.*@name@\([^@]\+\)@value@\$$\([0-9Xxa-fA-F-]\+\)@.*/\1 = \2/p' \
> >
> > imo we should use -E rather than doing \( \+ \) stuff so we get POSIX ERE
> > behavior by default
> 
> Is -E a GNU sed flag? I'm not seeing it on the man page, though -r
> seems to do the same thing.

yeah, -E was added a while ago for BSD compat but wasn't documented.  but since
POSIX has adopted -E, they've added it to the help/man/info pages in the latest
git version.  it's equiv to the -r flag.

although maybe using -r would be better since INSTALL:
	* GNU 'sed' 3.02 or newer
and the -E flag isn't in that version.  i don't think it was first available
until sed-4.2 which was released in 2009 (6 years ago).

> >> +# Install the Python pretty-printers.
> >> +py-const = $(patsubst %.pysym,%.py,$(gen-py-const-headers))
> >> +install-bin-script = $(py-const) nptl-printers.py
> >
> > why is this install-bin-script ?  we don't want to install these into /usr/bin/.
> > gdb pretty printers usually go into /usr/share/gdb/auto-load/<fspath>.  since
> > these are for the pthreads lib, you'd want like:
> >         /usr/share/gdb/auto-load/lib/libpthread.so.0
> > obviously the path & name will vary based on the install of libpthread.  we
> > probably also want to add a configure flag for the gdb autoload path as i
> > vaguely recall different distros using different base paths.
> 
> That'd be great. However, this would be best addressed by someone
> who's more familiar with the glibc build process, and Make and
> autoconf in general. I don't know how to fix this myself.

it would be better to not install this at all and leave it up to distro peeps
until we can get a proper rule for it.  i don't think we need to block merging
until the install step is ironed out.

> >> +++ b/nptl/nptl-printers.py
> >
> > at a high level, you should run this through pylint
> 
> I remember running pylint on this, but admittedly it was a long time ago :)

we've got scripts/pylint in the tree which uses the right rc file

> >> +        self.values.append(('Threads waiting for this condvar',
> >> +                           self.nwaiters >> COND_NWAITERS_SHIFT))
> >
> > trailing indent is wrong:
> > C:376, 0: Wrong continued indentation.
> >                            self.nwaiters >> COND_NWAITERS_SHIFT))
> >                            ^| (bad-continuation)
> 
> Did pylint report that? If so, how did you configure it? I'm asking
> this so I can fix any other errors that may pop out.

make sure you're using pylint-1.4+ ... the 0.x releases are crufty now
-mike
  

Patch

diff --git a/ChangeLog b/ChangeLog
index 23f6fe9..2d7931b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@ 
+2015-10-09  Martin Galvan  <martin.galvan@tallertechnologies.com>
+
+	* Makerules ($(common-objpfx)%.py): New target.
+	* nptl/Makefile (gen-py-const-headers): New define.
+	(install-bin-script): Add Python pretty printers.
+	($(addprefix $(objpfx),$(install-bin-script))): New target.
+	* nptl/nptl-printers.py: New file.
+	* nptl/nptl_lock_constants.pysym: New file.
+	* scripts/gen-py-const.awk: New file.
+
 2015-10-09  Adhemerval Zanella  <adhemerval.zanella@linaro.org>
 	    Phil Blundell <pb@pbcui.dot.net>

diff --git a/Makerules b/Makerules
index f9ca3f5..76ac0b9 100644
--- a/Makerules
+++ b/Makerules
@@ -190,6 +190,34 @@  sed-remove-dotdot := -e 's@  *\([^ 	\/$$][^ 	\]*\)@ $$(..)\1@g' \
 		     -e 's@^\([^ 	\/$$][^ 	\]*\)@$$(..)\1@g'
 endif

+ifdef gen-py-const-headers
+# This is a hack we use to generate .py files with constants for Python
+# pretty-printers. It works the same way as gen-as-const. See gen-py-const
+# for details on how the awk | gcc mechanism works.
+$(common-objpfx)%.py: $(..)scripts/gen-py-const.awk %.pysym $(common-before-compile)
+	$(AWK) -f $< $(filter %.pysym,$^)
+	$(AWK) -f $< $(filter %.pysym,$^) \
+		| $(CC) -S -o $(addsuffix .tmp,$@) $(CFLAGS) $(CPPFLAGS) -x c -
+	echo '# GENERATED FILE, DO NOT EDIT\n' > $@
+	echo '# Constant definitions for the NPTL pretty printers.' >> $@
+	echo '# See gen-py-const.awk for details.\n' >> $@
+
+	# Replace "@name@SOME_NAME@value@SOME_VALUE@" strings from the output of
+	# gen-py-const.awk with "SOME_NAME = SOME_VALUE" strings.
+	# The '-n' option, combined with the '/p' command, makes sed output only the
+	# modified lines instead of the whole input file. The output is redirected
+	# to a .py file; we'll import it from printers.py to read the constants
+	# generated by gen-py-const.awk.
+	# The regex has two capturing groups, for SOME_NAME and SOME_VALUE
+	# respectively. Notice SOME_VALUE will start with a '$'; Make requires that
+	# we write '$' twice.
+	sed -n 's/^.*@name@\([^@]\+\)@value@\$$\([0-9Xxa-fA-F-]\+\)@.*/\1 = \2/p' \
+		$(addsuffix .tmp,$@) >> $@
+	rm -f $(addsuffix .tmp,$@)
+
+before-compile += $(patsubst %.pysym,%.py,$(common-objpfx)$(gen-py-const-headers))
+generated += $(patsubst %.pysym,%.py,$(common-objpfx)$(gen-py-const-headers))
+endif  # gen-py-const-headers

 ifdef gen-as-const-headers
 # Generating headers for assembly constants.
diff --git a/nptl/Makefile b/nptl/Makefile
index 311b1a7..3ef7569 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -304,6 +304,7 @@  gen-as-const-headers = pthread-errnos.sym \
 		       lowlevelbarrier.sym unwindbuf.sym \
 		       lowlevelrobustlock.sym pthread-pi-defines.sym

+gen-py-const-headers = nptl_lock_constants.pysym

 LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst

@@ -412,6 +413,16 @@  ifneq ($(have-cxx-thread_local),yes)
 tests-unsupported += tst-thread_local1
 endif

+# Install the Python pretty-printers.
+py-const = $(patsubst %.pysym,%.py,$(gen-py-const-headers))
+install-bin-script = $(py-const) nptl-printers.py
+
+# Copy the .py files into $(objpfx) so that the install-bin-script target
+# in Makerules can find them.
+$(addprefix $(objpfx),$(install-bin-script)): $(common-objpfx)$(py-const) \
+					      nptl-printers.py
+	$(INSTALL) $^ $(objpfx)
+
 include ../Rules

 ifeq (yes,$(build-shared))
diff --git a/nptl/nptl-printers.py b/nptl/nptl-printers.py
new file mode 100644
index 0000000..640c08c
--- /dev/null
+++ b/nptl/nptl-printers.py
@@ -0,0 +1,671 @@ 
+# Copyright (C) 2015 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+# Contributed by Martin Galvan <martin.galvan@tallertechnologies.com>
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+"""Pretty printers for the NTPL lock types.
+
+This file contains the gdb pretty printers for the following types:
+
+    * pthread_mutex_t
+    * pthread_mutexattr_t
+    * pthread_cond_t
+    * pthread_condattr_t
+    * pthread_rwlock_t
+    * pthread_rwlockattr_t
+
+You can check which printers are registered and enabled by issuing the
+'info pretty-printer' gdb command. Printers should trigger automatically when
+trying to print a variable of one of the types mentioned above.
+"""
+
+import sys
+import re
+import ctypes
+import gdb
+from nptl_lock_constants import *
+
+class _IteratorP3(object):
+    """A simple Iterator class."""
+
+    def __init__(self, values):
+        self.values = values
+        self.length = len(self.values)
+        self.count = 0
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        count = self.count
+        self.count += 1
+
+        if count == self.length:
+            raise StopIteration
+        else:
+            return self.values[count]
+
+class _IteratorP2(_IteratorP3):
+    """Hack for Python 2 compatibilty."""
+
+    def next(self):
+        self.__next__()
+
+# Hack for Python 2 compatibilty
+if sys.version_info[0] > 2:
+    Iterator = _IteratorP3
+else:
+    Iterator = _IteratorP2
+
+################################################################################
+
+mutexTypesDict = {
+    PTHREAD_MUTEX_NORMAL: ('Type', 'Normal'),
+    PTHREAD_MUTEX_RECURSIVE: ('Type', 'Recursive'),
+    PTHREAD_MUTEX_ERRORCHECK: ('Type', 'Error check'),
+    PTHREAD_MUTEX_ADAPTIVE_NP: ('Type', 'Adaptive')
+}
+
+class MutexPrinter(object):
+    """Pretty printer for pthread_mutex_t."""
+
+    def __init__(self, mutex):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            mutex: A gdb.value representing a pthread_mutex_t.
+        """
+
+        data = mutex['__data']
+        self.lock = data['__lock']
+        self.count = data['__count']
+        self.owner = data['__owner']
+        self.kind = data['__kind']
+        self.values = []
+        self.readValues()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_mutex_t.
+        """
+
+        return 'pthread_mutex_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_mutex_t.
+        """
+
+        return Iterator(self.values)
+
+    def readValues(self):
+        """Read the mutex's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        self.readType()
+        self.readStatus()
+        self.readAttributes()
+        self.readMiscInfo()
+
+    def readType(self):
+        """Read the mutex's type."""
+
+        mutexType = self.kind & PTHREAD_MUTEX_KIND_MASK
+
+        # mutexType must be casted to int because it's a gdb.Value
+        self.values.append(mutexTypesDict[int(mutexType)])
+
+    def readStatus(self):
+        """Read the mutex's status.
+
+        For architectures which support lock elision, this method reads
+        whether the mutex is actually locked (i.e. it may show it as unlocked
+        even after calling pthread_mutex_lock).
+        """
+
+        if self.kind == PTHREAD_MUTEX_DESTROYED:
+            self.values.append(('Status', 'Destroyed'))
+        elif self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
+            self.readStatusRobust()
+        else:
+            self.readStatusNonRobust()
+
+    def readStatusRobust(self):
+        """Read the status of a robust mutex.
+
+        In glibc robust mutexes are implemented in a very different way than
+        non-robust ones. This method reads their locking status,
+        whether it may have waiters, their registered owner (if any),
+        whether the owner is alive or not, and the status of the state
+        they're protecting.
+        """
+
+        if self.lock == PTHREAD_MUTEX_UNLOCKED:
+            self.values.append(('Status', 'Unlocked'))
+        else:
+            if self.lock & FUTEX_WAITERS:
+                self.values.append(('Status', 'Locked, possibly with waiters'))
+            else:
+                self.values.append(('Status', 'Locked, no waiters'))
+
+            if self.lock & FUTEX_OWNER_DIED:
+                self.values.append(('Owner ID', '%d (dead)' % self.owner))
+            else:
+                self.values.append(('Owner ID', self.lock & FUTEX_TID_MASK))
+
+        if self.owner == PTHREAD_MUTEX_INCONSISTENT:
+            self.values.append(('State protected by this mutex',
+                                'Inconsistent'))
+        elif self.owner == PTHREAD_MUTEX_NOTRECOVERABLE:
+            self.values.append(('State protected by this mutex',
+                                'Not recoverable'))
+
+    def readStatusNonRobust(self):
+        """Read the status of a non-robust mutex.
+
+        Read info on whether the mutex is locked, if it may have waiters
+        and its owner (if any).
+        """
+
+        if self.lock == PTHREAD_MUTEX_UNLOCKED:
+            self.values.append(('Status', 'Unlocked'))
+        else:
+            if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
+                waiters = self.lock & FUTEX_WAITERS
+                owner = self.lock & FUTEX_TID_MASK
+            else:
+                # Mutex protocol is PP or none
+                waiters = (self.lock != PTHREAD_MUTEX_LOCKED_NO_WAITERS)
+                owner = self.owner
+
+            if waiters:
+                self.values.append(('Status', 'Locked, possibly with waiters'))
+            else:
+                self.values.append(('Status', 'Locked, no waiters'))
+
+            self.values.append(('Owner ID', owner))
+
+    def readAttributes(self):
+        """Read the mutex's attributes."""
+
+        if self.kind != PTHREAD_MUTEX_DESTROYED:
+            if self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
+                self.values.append(('Robust', 'Yes'))
+            else:
+                self.values.append(('Robust', 'No'))
+
+            # In glibc, robust mutexes always have their pshared flag set to
+            # 'shared' regardless of what the pshared flag of their
+            # mutexattr was. Therefore a robust mutex will act as shared even if
+            # it was initialized with a 'private' mutexattr.
+            if self.kind & PTHREAD_MUTEX_PSHARED_BIT:
+                self.values.append(('Shared', 'Yes'))
+            else:
+                self.values.append(('Shared', 'No'))
+
+            if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
+                self.values.append(('Protocol', 'Priority inherit'))
+            elif self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
+                prioCeiling = ((self.lock & PTHREAD_MUTEX_PRIO_CEILING_MASK) >>
+                               PTHREAD_MUTEX_PRIO_CEILING_SHIFT)
+
+                self.values.append(('Protocol', 'Priority protect'))
+                self.values.append(('Priority ceiling', prioCeiling))
+            else:
+                # PTHREAD_PRIO_NONE
+                self.values.append(('Protocol', 'None'))
+
+    def readMiscInfo(self):
+        """Read miscellaneous info on the mutex.
+
+        For now this reads the number of times a row a recursive mutex
+        was locked by the same thread.
+        """
+
+        mutexType = self.kind & PTHREAD_MUTEX_KIND_MASK
+
+        if mutexType == PTHREAD_MUTEX_RECURSIVE and self.count > 1:
+            self.values.append(('Times locked recursively', self.count))
+
+################################################################################
+
+class MutexAttributesPrinter(object):
+    """Pretty printer for pthread_mutexattr_t.
+
+    In the NPTL this is a type that's always casted to struct pthread_mutexattr,
+    which has a single 'mutexkind' field containing the actual attributes.
+    """
+
+    def __init__(self, mutexattr):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            mutexattr: A gdb.value representing a pthread_mutexattr_t.
+        """
+
+        mutexattrStruct = gdb.lookup_type('struct pthread_mutexattr')
+        self.mutexattr = mutexattr.cast(mutexattrStruct)['mutexkind']
+        self.values = []
+        self.readValues()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_mutexattr_t.
+        """
+
+        return 'pthread_mutexattr_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_mutexattr_t.
+        """
+
+        return Iterator(self.values)
+
+    def readValues(self):
+        """Read the mutexattr's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        mutexattrType = (self.mutexattr &
+                         ~PTHREAD_MUTEXATTR_FLAG_BITS &
+                         ~PTHREAD_MUTEX_NO_ELISION_NP)
+
+        # mutexattrType must be casted to int because it's a gdb.Value
+        self.values.append(mutexTypesDict[int(mutexattrType)])
+
+        if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_ROBUST:
+            self.values.append(('Robust', 'Yes'))
+        else:
+            self.values.append(('Robust', 'No'))
+
+        if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_PSHARED:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            self.values.append(('Shared', 'No'))
+
+        protocol = ((self.mutexattr & PTHREAD_MUTEXATTR_PROTOCOL_MASK) >>
+                    PTHREAD_MUTEXATTR_PROTOCOL_SHIFT)
+
+        if protocol == PTHREAD_PRIO_NONE:
+            self.values.append(('Protocol', 'None'))
+        elif protocol == PTHREAD_PRIO_INHERIT:
+            self.values.append(('Protocol', 'Priority inherit'))
+        elif protocol == PTHREAD_PRIO_PROTECT:
+            self.values.append(('Protocol', 'Priority protect'))
+
+################################################################################
+
+class ConditionVariablePrinter(object):
+    """Pretty printer for pthread_cond_t."""
+
+    def __init__(self, cond):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            cond: A gdb.value representing a pthread_cond_t.
+        """
+
+        data = cond['__data']
+        self.totalSeq = data['__total_seq']
+        self.mutex = data['__mutex']
+        self.nwaiters = data['__nwaiters']
+        self.values = []
+        self.readValues()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_cond_t.
+        """
+
+        return 'pthread_cond_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_cond_t.
+        """
+
+        return Iterator(self.values)
+
+    def readValues(self):
+        """Read the condvar's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        self.readStatus()
+        self.readAttributes()
+        self.readMutexInfo()
+
+    def readStatus(self):
+        """Read the status of the condvar.
+
+        This method reads whether the condvar is destroyed and how many threads
+        are waiting for it.
+        """
+
+        if self.totalSeq == PTHREAD_COND_DESTROYED:
+            self.values.append(('Status', 'Destroyed'))
+
+        self.values.append(('Threads waiting for this condvar',
+                           self.nwaiters >> COND_NWAITERS_SHIFT))
+
+    def readAttributes(self):
+        """Read the condvar's attributes."""
+
+        clockID = self.nwaiters & ((1 << COND_NWAITERS_SHIFT) - 1)
+        shared = (self.mutex == PTHREAD_COND_SHARED)
+
+        if shared:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            self.values.append(('Shared', 'No'))
+
+        self.values.append(('Clock ID', clockID))
+
+    def readMutexInfo(self):
+        """Read the data of the mutex this condvar is bound to.
+
+        A pthread_cond_t's __data.__mutex member is a void * which
+        must be casted to pthread_mutex_t *. For shared condvars, this
+        member isn't recorded and has a value of ~0l instead.
+        """
+
+        if self.mutex and self.mutex != PTHREAD_COND_SHARED:
+            mutexType = gdb.lookup_type('pthread_mutex_t')
+            mutex = self.mutex.cast(mutexType.pointer()).dereference()
+
+            self.values.append(('Mutex', mutex))
+
+################################################################################
+
+class ConditionVariableAttributesPrinter(object):
+    """Pretty printer for pthread_condattr_t.
+
+    In the NPTL this is a type that's
+    always casted to struct pthread_condattr, which has a single 'value' field
+    containing the actual attributes.
+    """
+
+    def __init__(self, condattr):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            condattr: A gdb.value representing a pthread_condattr_t.
+        """
+
+        condattrStruct = gdb.lookup_type('struct pthread_condattr')
+        self.condattr = condattr.cast(condattrStruct)['value']
+        self.values = []
+        self.readValues()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_condattr_t.
+        """
+
+        return 'pthread_condattr_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_condattr_t.
+        """
+
+        return Iterator(self.values)
+
+    def readValues(self):
+        """Read the condattr's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        clockID = self.condattr & ((1 << COND_NWAITERS_SHIFT) - 1)
+
+        if self.condattr & 1:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            self.values.append(('Shared', 'No'))
+
+        self.values.append(('Clock ID', clockID))
+
+################################################################################
+
+class RWLockPrinter(object):
+    """Pretty printer for pthread_rwlock_t."""
+
+    def __init__(self, rwlock):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            rwlock: A gdb.value representing a pthread_rwlock_t.
+        """
+
+        data = rwlock['__data']
+        self.readers = data['__nr_readers']
+        self.queuedReaders = data['__nr_readers_queued']
+        self.queuedWriters = data['__nr_writers_queued']
+        self.writerID = data['__writer']
+        self.shared = data['__shared']
+        self.prefersWriters = data['__flags']
+        self.values = []
+        self.readValues()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_rwlock_t.
+        """
+
+        return 'pthread_rwlock_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_rwlock_t.
+        """
+
+        return Iterator(self.values)
+
+    def readValues(self):
+        """Read the rwlock's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        self.readStatus()
+        self.readAttributes()
+
+    def readStatus(self):
+        """Read the status of the rwlock."""
+
+        if self.writerID:
+            self.values.append(('Status', 'Locked (Write)'))
+            self.values.append(('Writer ID', self.writerID))
+        elif self.readers:
+            self.values.append(('Status', 'Locked (Read)'))
+            self.values.append(('Readers', self.readers))
+        else:
+            self.values.append(('Status', 'Unlocked'))
+
+        self.values.append(('Queued readers', self.queuedReaders))
+        self.values.append(('Queued writers', self.queuedWriters))
+
+    def readAttributes(self):
+        """Read the attributes of the rwlock."""
+
+        if self.shared:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            self.values.append(('Shared', 'No'))
+
+        if self.prefersWriters:
+            self.values.append(('Prefers', 'Writers'))
+        else:
+            self.values.append(('Prefers', 'Readers'))
+
+################################################################################
+
+class RWLockAttributesPrinter(object):
+    """Pretty printer for pthread_rwlockattr_t.
+
+    In the NPTL this is a type that's always casted to
+    struct pthread_rwlockattr, which has two fields ('lockkind' and 'pshared')
+    containing the actual attributes.
+    """
+
+    def __init__(self, rwlockattr):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            rwlockattr: A gdb.value representing a pthread_rwlockattr_t.
+        """
+
+        rwlockattrStruct = gdb.lookup_type('struct pthread_rwlockattr')
+        self.rwlockattr = rwlockattr.cast(rwlockattrStruct)
+        self.values = []
+        self.readValues()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_rwlockattr_t."""
+
+        return 'pthread_rwlockattr_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_rwlockattr_t."""
+
+        return Iterator(self.values)
+
+    def readValues(self):
+        """Read the rwlockattr's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        rwlockType = self.rwlockattr['lockkind']
+        shared = self.rwlockattr['pshared']
+
+        if shared == PTHREAD_PROCESS_SHARED:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            # PTHREAD_PROCESS_PRIVATE
+            self.values.append(('Shared', 'No'))
+
+        if rwlockType == PTHREAD_RWLOCK_PREFER_READER_NP:
+            self.values.append(('Prefers', 'Readers'))
+        elif (rwlockType == PTHREAD_RWLOCK_PREFER_WRITER_NP or
+              rwlockType == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP):
+            self.values.append(('Prefers', 'Writers'))
+
+################################################################################
+
+class Printer(object):
+    """Printer class which conforms to the gdb pretty printing interface."""
+
+    def __init__(self, name):
+        self.name = name
+        self.enabled = True
+        self.subprinters = []
+
+    class Subprinter(object):
+        """A regex-based printer.
+
+        Individual pretty-printers are registered as subprinters of a single
+        Printer instance.
+        """
+
+        def __init__(self, name, regex, callable):
+            """
+            Initialize a pretty-printer.
+
+            Args:
+                name: The name of the printer.
+                regex: A regular expression. When gdb tries to print a variable
+                    whose type matches the regex it'll trigger this printer.
+                callable: A function or callable object that gdb will call
+                    when trying to print some value. It should return a
+                    pretty-printer.
+            """
+            self.name = name
+            self.regex = re.compile(regex)
+            self.callable = callable
+            self.enabled = True
+
+    def addSubprinter(self, name, regex, callable):
+        """Register a regex-based subprinter."""
+
+        self.subprinters.append(self.Subprinter(name, regex, callable))
+
+    def __call__(self, value):
+        """gdb API function.
+
+        This is called when trying to print an inferior value
+        from gdb. If a registered printer's regex matches the value's type,
+        gdb will use the printer to print the value.
+        """
+
+        typeName = value.type.name
+
+        if typeName:
+            for subprinter in self.subprinters:
+                if subprinter.enabled and subprinter.regex.match(typeName):
+                    return subprinter.callable(value)
+
+        # Return None if we have no type name or if we can't find a subprinter
+        # for the given type.
+        return None
+
+################################################################################
+
+def register(objfile):
+    """Register the pretty printers within the given objfile."""
+
+    printer = Printer('Glibc pthread locks')
+
+    printer.addSubprinter('pthread_mutex_t', '^pthread_mutex_t$', MutexPrinter)
+    printer.addSubprinter('pthread_mutexattr_t', '^pthread_mutexattr_t$',
+                          MutexAttributesPrinter)
+    printer.addSubprinter('pthread_cond_t', '^pthread_cond_t$',
+                          ConditionVariablePrinter)
+    printer.addSubprinter('pthread_condattr_t', '^pthread_condattr_t$',
+                          ConditionVariableAttributesPrinter)
+    printer.addSubprinter('pthread_rwlock_t', '^pthread_rwlock_t$', RWLockPrinter)
+    printer.addSubprinter('pthread_rwlockattr_t', '^pthread_rwlockattr_t$',
+                          RWLockAttributesPrinter)
+
+    gdb.printing.register_pretty_printer(objfile, printer)
+
+register(gdb.current_objfile())
diff --git a/nptl/nptl_lock_constants.pysym b/nptl/nptl_lock_constants.pysym
new file mode 100644
index 0000000..c576822
--- /dev/null
+++ b/nptl/nptl_lock_constants.pysym
@@ -0,0 +1,66 @@ 
+#include <pthreadP.h>
+
+-- Mutex types
+PTHREAD_MUTEX_KIND_MASK          PTHREAD_MUTEX_KIND_MASK_NP
+PTHREAD_MUTEX_NORMAL
+PTHREAD_MUTEX_RECURSIVE          PTHREAD_MUTEX_RECURSIVE_NP
+PTHREAD_MUTEX_ERRORCHECK         PTHREAD_MUTEX_ERRORCHECK_NP
+PTHREAD_MUTEX_ADAPTIVE_NP
+
+-- Mutex status
+-- These are hardcoded all over the code; there are no enums/macros for them.
+PTHREAD_MUTEX_DESTROYED         -1
+PTHREAD_MUTEX_UNLOCKED           0
+PTHREAD_MUTEX_LOCKED_NO_WAITERS  1
+
+-- For robust mutexes
+PTHREAD_MUTEX_INCONSISTENT
+PTHREAD_MUTEX_NOTRECOVERABLE
+FUTEX_OWNER_DIED
+
+-- For robust and PI mutexes
+FUTEX_WAITERS
+FUTEX_TID_MASK
+
+-- Mutex attributes
+PTHREAD_MUTEX_ROBUST_NORMAL_NP
+PTHREAD_MUTEX_PRIO_INHERIT_NP
+PTHREAD_MUTEX_PRIO_PROTECT_NP
+PTHREAD_MUTEX_PSHARED_BIT
+PTHREAD_MUTEX_PRIO_CEILING_SHIFT
+PTHREAD_MUTEX_PRIO_CEILING_MASK
+
+-- Mutex attribute flags
+PTHREAD_MUTEXATTR_PROTOCOL_SHIFT
+PTHREAD_MUTEXATTR_PROTOCOL_MASK
+PTHREAD_MUTEXATTR_PRIO_CEILING_MASK
+PTHREAD_MUTEXATTR_FLAG_ROBUST
+PTHREAD_MUTEXATTR_FLAG_PSHARED
+PTHREAD_MUTEXATTR_FLAG_BITS
+PTHREAD_MUTEX_NO_ELISION_NP
+
+-- Priority protocols
+PTHREAD_PRIO_NONE
+PTHREAD_PRIO_INHERIT
+PTHREAD_PRIO_PROTECT
+
+-- These values are hardcoded as well:
+-- Value of __mutex for shared condvars.
+PTHREAD_COND_SHARED             ~0l
+
+-- Value of __total_seq for destroyed condvars.
+PTHREAD_COND_DESTROYED          -1ull
+
+-- __nwaiters encodes the number of threads waiting on a condvar
+-- and the clock ID.
+-- __nwaiters >> COND_NWAITERS_SHIFT gives us the number of waiters.
+COND_NWAITERS_SHIFT
+
+-- Rwlock attributes
+PTHREAD_RWLOCK_PREFER_READER_NP
+PTHREAD_RWLOCK_PREFER_WRITER_NP
+PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
+
+-- 'Shared' attribute values
+PTHREAD_PROCESS_PRIVATE
+PTHREAD_PROCESS_SHARED
diff --git a/scripts/gen-py-const.awk b/scripts/gen-py-const.awk
new file mode 100644
index 0000000..5fd2b8d
--- /dev/null
+++ b/scripts/gen-py-const.awk
@@ -0,0 +1,116 @@ 
+# Copyright (C) 2015 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+# Contributed by Martin Galvan <martin.galvan@tallertechnologies.com>
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+# This script is a smaller version of the clever gen-asm-const.awk hack used to
+# generate ASM constants from .sym files. We'll use this to generate constants
+# for Python pretty-printers.
+#
+# The input to this script are .pysym files that look like:
+# #C_Preprocessor_Directive...
+# NAME1
+# NAME2 expression...
+#
+# A line giving just a name implies an expression consisting of just that name.
+# Comments start with '--'.
+#
+# The output of this script is a 'dummy' function containing 'asm' declarations
+# for each non-preprocessor line in the .pysym file. The expression values will
+# appear as input operands to the 'asm' declaration. For example, if we have:
+#
+# /* header.h */
+# #define MACRO 42
+#
+# struct S {
+#     char c1;
+#     char c2;
+#     char c3;
+# };
+#
+# enum E {
+#     ZERO,
+#     ONE
+# };
+#
+# /* symbols.pysym */
+# #include <stddef.h>
+# #include "header.h"
+# -- This is a comment
+# MACRO
+# C3_OFFSET offsetof(struct S, c3)
+# E_ONE ONE
+#
+# the output will be:
+#
+# #include <stddef.h>
+# #include "header.h"
+# void dummy(void)
+# {
+#   asm ("@name@MACRO@value@%0@" : : "i" (MACRO));
+#   asm ("@name@C3_OFFSET@value@%0@" : : "i" (offsetof(struct S, c3)));
+#   asm ("@name@E_ONE@value@%0@" : : "i" (ONE));
+# }
+#
+# We'll later feed this output to gcc -S. Since '-S' tells gcc to compile but
+# not assemble, gcc will output something like:
+#
+# dummy:
+# 	...
+# 	@name@MACRO@value@$42@
+# 	@name@C3_OFFSET@value@$2@
+# 	@name@E_ONE@value@$1@
+#
+# Finally, we can process that output to extract the constant values.
+# Notice gcc will prepend a '$' to each value.
+
+# found_symbol indicates whether we found a non-comment, non-preprocessor line.
+BEGIN { found_symbol = 0 }
+
+# C preprocessor directives go straight through.
+/^#/ { print; next; }
+
+# Skip comments.
+/--/ { next; }
+
+# Trim leading whitespace.
+{ sub(/^[[:blank:]]*/, ""); }
+
+# If we found a non-comment, non-preprocessor line, print the 'dummy' function
+# header.
+NF > 0 && !found_symbol {
+    print "void dummy(void)\n{";
+    found_symbol = 1;
+}
+
+# If the line contains just a name, duplicate it so we can use that name
+# as the value of the expression.
+NF == 1 { sub(/^.*$/, "& &"); }
+
+# If a line contains a name and an expression...
+NF > 1 {
+    name = $1;
+
+    # Remove any characters before the second field.
+    sub(/^[^[:blank:]]+[[:blank:]]+/, "");
+
+    # '$0' ends up being everything that appeared after the first field
+    # separator.
+    printf "  asm (\"@name@%s@value@%0@\" : : \"i\" (%s));\n", name, $0;
+}
+
+# Close the 'dummy' function.
+END { if (found_symbol) print "}"; }