瀏覽代碼

Dpkg::Shlibs::Cppfilt: new interface to the c++filt utility.

This new module can be used to do symbol demangling with the c++filt
utility. For performance reasons, the c++filt program is run
in the background and used as a daemon: for each line written
it immediately outputs the demangled line.

The module also takes care of the c++filt process management
(forking and termination).

Signed-off-by: Modestas Vainius <modax@debian.org>
Signed-off-by: Raphaël Hertzog <hertzog@debian.org>
Modestas Vainius 16 年之前
父節點
當前提交
3a4c2b071c
共有 3 個文件被更改,包括 197 次插入0 次删除
  1. 106 0
      scripts/Dpkg/Shlibs/Cppfilt.pm
  2. 2 0
      scripts/Makefile.am
  3. 89 0
      scripts/t/190_Dpkg_Shlibs_Cppfilt.t

+ 106 - 0
scripts/Dpkg/Shlibs/Cppfilt.pm

@@ -0,0 +1,106 @@
+# Copyright © 2009-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+package Dpkg::Shlibs::Cppfilt;
+
+use strict;
+use warnings;
+use base 'Exporter';
+
+use Dpkg::ErrorHandling;
+use Dpkg::IPC;
+use IO::Handle;
+
+our @EXPORT = qw(cppfilt_demangle);
+
+# A hash of 'objects' referring to preforked c++filt processes for the distinct
+# demangling types.
+my %cppfilts;
+
+sub get_cppfilt {
+    my $type = shift || "auto";
+
+    # Fork c++filt process for demangling $type unless it is forked already.
+    # Keeping c++filt running improves performance a lot.
+    my $filt;
+    if (exists $cppfilts{$type}) {
+	$filt = $cppfilts{$type};
+    } else {
+	$filt = { from => undef, to => undef,
+	            last_symbol => "", last_result => "" };
+	$filt->{pid} = fork_and_exec(exec => [ 'c++filt',
+	                                         '--no-verbose',
+	                                         "--format=$type" ],
+	                              from_pipe => \$filt->{from},
+	                              to_pipe => \$filt->{to});
+	internerr(_g("unable to execute c++filt")) unless defined $filt->{from};
+	$filt->{from}->autoflush(1);
+
+	$cppfilts{$type} = $filt;
+    }
+    return $filt;
+}
+
+# Demangle the given $symbol using demangler for the specified $type (defaults
+# to 'auto') . Extraneous characters trailing after a mangled name are kept
+# intact. If neither whole $symbol nor portion of it could be demangled, undef
+# is returned.
+sub cppfilt_demangle {
+    my $symbol = shift;
+    my $type = shift;
+
+    # Start or get c++filt 'object' for the requested type.
+    my $filt = get_cppfilt($type);
+
+    # Remember the last result. Such a local optimization is cheap and useful
+    # when sequential pattern matching is performed.
+    if ($filt->{last_symbol} ne $symbol) {
+	# This write/read operation should not deadlock because c++filt flushes
+	# output buffer on LF or each invalid character.
+	print { $filt->{from} } $symbol, "\n";
+	my $demangled = readline($filt->{to});
+	chop $demangled;
+
+	# If the symbol was not demangled, return undef
+	$demangled = undef if $symbol eq $demangled;
+
+	# Remember the last result
+	$filt->{last_symbol} = $symbol;
+	$filt->{last_result} = $demangled;
+    }
+    return $filt->{last_result};
+}
+
+sub terminate_cppfilts {
+    foreach (keys %cppfilts) {
+	next if not defined $cppfilts{$_}{pid};
+	close $cppfilts{$_}{from};
+	close $cppfilts{$_}{to};
+	wait_child($cppfilts{$_}{pid}, "cmdline" => "c++filt",
+	                               "nocheck" => 1,
+	                               "timeout" => 5);
+	delete $cppfilts{$_};
+    }
+}
+
+# Close/terminate running c++filt process(es)
+END {
+    # Make sure exitcode is not changed (by wait_child)
+    my $exitcode = $?;
+    terminate_cppfilts();
+    $? = $exitcode;
+}
+
+1;

+ 2 - 0
scripts/Makefile.am

@@ -79,6 +79,7 @@ nobase_dist_perllib_DATA = \
 	Dpkg/Shlibs/Objdump.pm \
 	Dpkg/Shlibs/Symbol.pm \
 	Dpkg/Shlibs/SymbolFile.pm \
+	Dpkg/Shlibs/Cppfilt.pm \
 	Dpkg/Source/Archive.pm \
 	Dpkg/Source/CompressedFile.pm \
 	Dpkg/Source/Compressor.pm \
@@ -137,6 +138,7 @@ test_tmpdir = t.tmp
 test_cases = \
 	t/000_pod.t \
 	t/100_Dpkg_Version.t \
+	t/190_Dpkg_Shlibs_Cppfilt.t \
 	t/200_Dpkg_Shlibs.t \
 	t/300_Dpkg_BuildOptions.t \
 	t/400_Dpkg_Deps.t \

+ 89 - 0
scripts/t/190_Dpkg_Shlibs_Cppfilt.t

@@ -0,0 +1,89 @@
+# -*- mode: cperl;-*-
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+use Test::More tests => 124;
+
+use strict;
+use warnings;
+
+use_ok("Dpkg::Shlibs::Cppfilt");
+
+sub demangle_cpp {
+    return cppfilt_demangle(shift, 'gnu-v3');
+}
+
+# Simple C++ demangling tests
+is ( demangle_cpp('_ZNSt10istrstreamC1EPKcl'),
+                  'std::istrstream::istrstream(char const*, long)',
+    'demangle symbol' );
+is ( demangle_cpp('_ZNSt10istrstreamC1EPKcl@Base'),
+                  'std::istrstream::istrstream(char const*, long)@Base',
+    'demangle symbol with extra postfix' );
+is ( demangle_cpp('foobar _ZNSt10istrstreamC1EPKcl@Base'),
+                  'foobar std::istrstream::istrstream(char const*, long)@Base',
+    'demangle symbol with garbage around it' );
+is ( demangle_cpp('FoobarInvalidSymbol'), undef,
+    'non-demanglable string' );
+
+# Mass C++ demangling. Checking if c++filt does not hang and cppfilt_demangle()
+# immediatelly provides a correct answer to the caller (i.e. no buffering).
+my @mangledtext = split(/\n+/s, <<'END');
+0000000000000000      DF *UND*  0000000000000000  GCC_3.0     _Unwind_SetIP
+0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __towlower_l
+0000000000000000      DO *UND*  0000000000000000  GLIBC_2.2.5 stdout
+0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 wmemset
+0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fflush
+0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 getc
+0000000000000000  w   D  *UND*  0000000000000000              pthread_join
+00000000000cfc22 g    DO .rodata        0000000000000001  GLIBCXX_3.4 _ZNSt14numeric_limitsIyE17has_signaling_NaNE
+0000000000088d80  w   DF .text  0000000000000064  GLIBCXX_3.4 _ZNSt11__timepunctIcEC2Em
+00000000002f40a0  w   DO .data.rel.ro   0000000000000020  GLIBCXX_3.4 _ZTTSt14basic_ifstreamIwSt11char_traitsIwEE
+000000000005a5f0 g    DF .text  0000000000000063  GLIBCXX_3.4.11 _ZNVSt9__atomic011atomic_flag5clearESt12memory_order
+00000000000bdc20  w   DF .text  0000000000000024  GLIBCXX_3.4 _ZNSbIwSt11char_traitsIwESaIwEEC1EPKwmRKS1_
+0000000000063850 g    DF .text  0000000000000062  GLIBCXX_3.4 _ZNSt5ctypeIwED2Ev
+00000000000898b0  w   DF .text  0000000000000255  GLIBCXX_3.4 _ZNKSt8time_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE3putES3_RSt8ios_basecPK2tmPKcSB_
+
+00000000000bff30 g    DF .text  0000000000000019  _ZNSt18condition_variable10notify_oneEv@GLIBCXX_3.4.11
+00000000000666a0 g    DF .text  000000000000003f  _ZNKSt3tr14hashIRKSbIwSt11char_traitsIwESaIwEEEclES6_@GLIBCXX_3.4.10
+00000000002f6160  w   DO .data.rel.ro   0000000000000050  _ZTTSt18basic_stringstreamIcSt11char_traitsIcESaIcEE@GLIBCXX_3.4
+END
+
+my @demangledtext = split(/\n+/s, <<'END');
+0000000000000000      DF *UND*  0000000000000000  GCC_3.0     _Unwind_SetIP
+0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __towlower_l
+0000000000000000      DO *UND*  0000000000000000  GLIBC_2.2.5 stdout
+0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 wmemset
+0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fflush
+0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 getc
+0000000000000000  w   D  *UND*  0000000000000000              pthread_join
+00000000000cfc22 g    DO .rodata        0000000000000001  GLIBCXX_3.4 std::numeric_limits<unsigned long long>::has_signaling_NaN
+0000000000088d80  w   DF .text  0000000000000064  GLIBCXX_3.4 std::__timepunct<char>::__timepunct(unsigned long)
+00000000002f40a0  w   DO .data.rel.ro   0000000000000020  GLIBCXX_3.4 VTT for std::basic_ifstream<wchar_t, std::char_traits<wchar_t> >
+000000000005a5f0 g    DF .text  0000000000000063  GLIBCXX_3.4.11 std::__atomic0::atomic_flag::clear(std::memory_order) volatile
+00000000000bdc20  w   DF .text  0000000000000024  GLIBCXX_3.4 std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::basic_string(wchar_t const*, unsigned long, std::allocator<wchar_t> const&)
+0000000000063850 g    DF .text  0000000000000062  GLIBCXX_3.4 std::ctype<wchar_t>::~ctype()
+00000000000898b0  w   DF .text  0000000000000255  GLIBCXX_3.4 std::time_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, tm const*, char const*, char const*) const
+
+00000000000bff30 g    DF .text  0000000000000019  std::condition_variable::notify_one()@GLIBCXX_3.4.11
+00000000000666a0 g    DF .text  000000000000003f  std::tr1::hash<std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > const&>::operator()(std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > const&) const@GLIBCXX_3.4.10
+00000000002f6160  w   DO .data.rel.ro   0000000000000050  VTT for std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >@GLIBCXX_3.4
+END
+
+for (my $try = 1; $try <= 7; $try++) {
+    for (my $i = 0; $i <= $#mangledtext; $i++) {
+	my $demangled = demangle_cpp($mangledtext[$i]) || $mangledtext[$i];
+	is( $demangled, $demangledtext[$i], "mass c++ demangling (${try}x".(${i}+1).")");
+    }
+}