diff --git a/AUTHORS b/AUTHORS index a8d0fae5..1e844ddc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,3 @@ -Søren Sandmann (sandmann@redhat.com) +# Generated by Makefile. Do not edit. + +Christian Hergert diff --git a/COPYING.gpl-2 b/COPYING.gpl-2 new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/COPYING.gpl-2 @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 00000000..44e2dde8 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,27 @@ +# Sysprof 2.x + +## Design + +### libsysprof-2 + +This library provides various UI components and profiling tools as a library. +It is intended to be used from IDEs such as GNOME Builder. + +### sysprofd + +Sometimes, your current user does not have the required capabilities +such as (`CAP_SYS_ADMIN`) to access performance counters. This daemon provides +a way to get access to those counters while going through the normal +authorization flow users are familiar with. + +### sysprof-cli + +This is a command line tool to help you capture in an automated fashion or +when you don't have access to a UI or want to remove the UI overhead from +system traces. + +### sysprof + +This is the tranditional Sysprof interface. It uses libsysprof-2 to setup +and manage performance counters and display results. + diff --git a/Makefile.am b/Makefile.am index 8be948df..b5aeb76e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,122 +1,37 @@ -ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +SUBDIRS = daemon data help lib po src tools tests -#SUBDIRS = $(MODULE_SUBDIR) -#DIST_SUBDIRS = module +EXTRA_DIST = AUTHORS -bin_PROGRAMS = sysprof-cli +DISTCHECK_CONFIGURE_FLAGS = --with-systemdsystemunitdir='$(prefix)/etc/systemd/system' -if BUILD_GUI -bin_PROGRAMS += sysprof -endif +AUTHORS: + $(AM_V_GEN)if test -d "$(srcdir)/.git"; \ + then \ + echo Creating $@ && \ + ( cd "$(top_srcdir)" && \ + echo '# Generated by Makefile. Do not edit.'; echo; \ + git log --no-merges --pretty=format:"%an" $(SUBDIRS) \ + | sort | uniq ) > $@.tmp \ + && mv -f $@.tmp $@ \ + || ( rm -f $@.tmp ; \ + echo Failed to generate $@ >&2 ); \ + else touch $@; fi -SYSPROF_CORE = \ - binfile.h \ - binfile.c \ - collector.c \ - collector.h \ - demangle.c \ - elfparser.c \ - elfparser.h \ - profile.h \ - profile.c \ - sfile.h \ - sfile.c \ - sformat.h \ - sformat.c \ - stackstash.h \ - stackstash.c \ - tracker.h \ - tracker.c \ - unwind.h \ - unwind.c \ - watch.h \ - watch.c \ - \ - util.h +# Generate the ChangeLog. +@GENERATE_CHANGELOG_RULES@ +dist-hook: dist-ChangeLog -# module/sysprof-module.h +GITIGNOREFILES = \ + **/*.swp \ + *.o \ + aclocal.m4 \ + build-aux \ + ChangeLog \ + config \ + config.h.in \ + gtk-doc.m4 \ + gtk-doc.make \ + INSTALL \ + $(NULL) -# -# GUI version -# -if BUILD_GUI - -sysprof_SOURCES = \ - $(SYSPROF_CORE) \ - footreestore.c \ - footreestore.h \ - footreedatalist.h \ - footreedatalist.c \ - treeviewutils.h \ - treeviewutils.c \ - sysprof.c - -sysprof_CPPFLAGS = \ - $(GUI_DEP_CFLAGS) \ - -DDATADIR=\"$(pkgdatadir)\" \ - -DPIXMAPDIR=\"$(pixmapsdir)\" - -sysprof_LDADD = $(GUI_DEP_LIBS) - -endif - -udevdir = $(sysconfdir)/udev/rules.d -dist_udev_DATA = 60-sysprof.rules - -pixmapsdir = $(datadir)/pixmaps - -dist_pkgdata_DATA = sysprof.glade -dist_pixmaps_DATA = sysprof-icon-16.png sysprof-icon-24.png sysprof-icon-32.png sysprof-icon-48.png sysprof-icon-256.png - -# -# Command line version -# - -sysprof_cli_SOURCES = \ - $(SYSPROF_CORE) \ - signal-handler.h \ - signal-handler.c \ - sysprof-cli.c - -sysprof_cli_CPPFLAGS = \ - $(CORE_DEP_CFLAGS) - -sysprof_cli_LDADD = $(CORE_DEP_LIBS) - -EXTRA_DIST = \ - sysprof-icon-source.svg - -# -# Test programs -# -noinst_PROGRAMS = testelf testunwind testdemangle - -# testunwind -testunwind_SOURCES = \ - testunwind.c \ - demangle.c \ - elfparser.c \ - elfparser.h \ - unwind.c \ - unwind.h -testunwind_CPPFLAGS = $(CORE_DEP_CFLAGS) -testunwind_LDADD = $(CORE_DEP_LIBS) - -# testelf -testelf_SOURCES = \ - testelf.c \ - demangle.c \ - elfparser.c \ - elfparser.h - -testelf_CPPFLAGS = $(CORE_DEP_CFLAGS) -testelf_LDADD = $(CORE_DEP_LIBS) - -# testdemangle -testdemangle_SOURCES = \ - testdemangle.c \ - elfparser.c \ - elfparser.h \ - demangle.c -testdemangle_CPPFLAGS = $(CORE_DEP_CFLAGS) -testdemangle_LDADD = $(CORE_DEP_LIBS) +-include $(top_srcdir)/git.mk diff --git a/NEWS b/NEWS index b0b77200..e69de29b 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +0,0 @@ -- New 'everything' object -- New commandline version [Lorenzo Colitti] -- Assign time spent in kernel to the user process responsible -- New screenshot window -- Device based on udev [Kristian Hoegsberg] -- Port to RHEL4 [Bastien Nocera] -- Performance improvements diff --git a/TODO b/TODO new file mode 100644 index 00000000..5812353a --- /dev/null +++ b/TODO @@ -0,0 +1,25 @@ +# TODO + + - Follow fork/threads + + Currently, when watching a pid, we do not have a way to follow forks. + We need to watch FORK/EXIT events and create/dispose additional perf + streams from the kernel. + + - Add cpu/mem/net data source + + - Add frame types for generic counters + + - Add support for visualizations other than the callgraph. + + 1) I'd like to see a graph of call depth over time. + 2) line graphs for cpu/mem/net. + 3) disk I/O + + etc. + + One quick way to put together the UI is to just use ListBox for the + rows. Then we need a "graph" child that allows drawing. The nice thing + here is that we get pixel caching for free. Resizing the row height + gets a little annoying though. + diff --git a/autogen.sh b/autogen.sh index 095dd7c5..abadb9ad 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,21 +1,37 @@ #!/bin/sh # Run this to generate all the initial makefiles, etc. - -test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=$(dirname "$0") test -n "$srcdir" || srcdir=. -olddir=`pwd` -cd "$srcdir" +olddir=$(pwd) -mkdir m4 +cd $srcdir -AUTORECONF=`which autoreconf` -if test -z $AUTORECONF; then - echo "*** No autoreconf found, please install it ***" - exit 1 -else - autoreconf --force --install --verbose || exit $? +(test -f configure.ac) || { + echo "*** ERROR: Directory '$srcdir' does not look like the top-level project directory ***" + exit 1 +} + +# shellcheck disable=SC2016 +PKG_NAME=$(autoconf --trace 'AC_INIT:$1' configure.ac) + +if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then + echo "*** WARNING: I am going to run 'configure' with no arguments." >&2 + echo "*** If you wish to pass any to it, please specify them on the" >&2 + echo "*** '$0' command line." >&2 + echo "" >&2 fi +aclocal --install || exit 1 +autoreconf --verbose --force --install || exit 1 + cd "$olddir" -test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" +if [ "$NOCONFIGURE" = "" ]; then + $srcdir/configure "$@" || exit 1 + + if [ "$1" = "--help" ]; then exit 0 else + echo "Now type 'make' to compile $PKG_NAME" || exit 1 + fi +else + echo "Skipping configure process." +fi diff --git a/build-aux/tap-test b/build-aux/tap-test new file mode 100755 index 00000000..481e333e --- /dev/null +++ b/build-aux/tap-test @@ -0,0 +1,5 @@ +#! /bin/sh + +# run a GTest in tap mode. The test binary is passed as $1 + +$1 -k --tap diff --git a/build-aux/test-driver b/build-aux/test-driver new file mode 100755 index 00000000..8e575b01 --- /dev/null +++ b/build-aux/test-driver @@ -0,0 +1,148 @@ +#! /bin/sh +# test-driver - basic testsuite driver script. + +scriptversion=2013-07-13.22; # UTC + +# Copyright (C) 2011-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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +usage_error () +{ + echo "$0: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat <$log_file 2>&1 +estatus=$? + +if test $enable_hard_errors = no && test $estatus -eq 99; then + tweaked_estatus=1 +else + tweaked_estatus=$estatus +fi + +case $tweaked_estatus:$expect_failure in + 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; + 0:*) col=$grn res=PASS recheck=no gcopy=no;; + 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; + 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; + *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; + *:*) col=$red res=FAIL recheck=yes gcopy=yes;; +esac + +# Report the test outcome and exit status in the logs, so that one can +# know whether the test passed or failed simply by looking at the '.log' +# file, without the need of also peaking into the corresponding '.trs' +# file (automake bug#11814). +echo "$res $test_name (exit status: $estatus)" >>$log_file + +# Report outcome to console. +echo "${col}${res}${std}: $test_name" + +# Register the test result, and other relevant metadata. +echo ":test-result: $res" > $trs_file +echo ":global-test-result: $res" >> $trs_file +echo ":recheck: $recheck" >> $trs_file +echo ":copy-in-global-log: $gcopy" >> $trs_file + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/configure.ac b/configure.ac index ad3b786d..124649d4 100644 --- a/configure.ac +++ b/configure.ac @@ -1,34 +1,113 @@ -AC_PREREQ([2.63]) +AC_PREREQ([2.69]) -AC_INIT([sysprof], [1.3.1]) +dnl *********************************************************************** +dnl Define Versioning Information +dnl *********************************************************************** +m4_define([major_version],[3]) +m4_define([minor_version],[19]) +m4_define([micro_version],[90]) +m4_define([package_version],[major_version.minor_version.micro_version]) +m4_define([bug_report_url],[https://bugzilla.gnome.org/enter_bug.cgi?product=sysprof]) +m4_define([api_version],[2]) + +AX_IS_RELEASE([micro-version]) + + +dnl *********************************************************************** +dnl Initialize autoconf +dnl *********************************************************************** +AC_INIT([sysprof],[package_version],[bug_report_url]) AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_SRCDIR([sysprof.glade]) +AC_CONFIG_SRCDIR([data/sysprof.pc.in]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) +AC_SUBST([ACLOCAL_AMFLAGS], "-I m4") +AC_CANONICAL_HOST -AM_INIT_AUTOMAKE([1.10 -Wall no-define]) + +dnl *********************************************************************** +dnl Make version information available to autoconf files +dnl *********************************************************************** +AC_SUBST([MAJOR_VERSION],major_version) +AC_SUBST([MINOR_VERSION],minor_version) +AC_SUBST([MICRO_VERSION],micro_version) +AC_SUBST([API_VERSION],api_version) + + +dnl *********************************************************************** +dnl Initialize automake +dnl *********************************************************************** +AM_SILENT_RULES([yes]) +AM_INIT_AUTOMAKE([1.11 foreign subdir-objects tar-ustar no-dist-gzip dist-xz -Wno-portability]) AM_MAINTAINER_MODE([enable]) +AX_GENERATE_CHANGELOG -# Support silent build rules, requires at least automake-1.11. Disable -# by either passing --disable-silent-rules to configure or passing V=1 -# to make -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -# Check for programs +dnl *********************************************************************** +dnl Add extra debugging with --enable-debug and --enable-compile-warnings +dnl *********************************************************************** +AX_CHECK_ENABLE_DEBUG([no],[] + [G_DISABLE_ASSERT G_DISABLE_CHECKS G_DISABLE_CAST_CHECKS]) +AX_COMPILER_FLAGS + + +dnl *********************************************************************** +dnl Internationalization +dnl *********************************************************************** +GETTEXT_PACKAGE=AC_PACKAGE_TARNAME +AC_SUBST([GETTEXT_PACKAGE]) +AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [GETTEXT package name]) + +AM_GNU_GETTEXT_VERSION([0.19.6]) +AM_GNU_GETTEXT([external]) + + +dnl *********************************************************************** +dnl Check for required programs +dnl *********************************************************************** +AX_COMPILER_FLAGS_CXXFLAGS AC_PROG_CC -AM_PROG_CC_C_O AC_PROG_INSTALL +AX_CXX_COMPILE_STDCXX_0X +AC_PATH_PROG([GLIB_GENMARSHAL],[glib-genmarshal]) +AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums]) +AC_PATH_PROG([GLIB_COMPILE_RESOURCES],[glib-compile-resources]) +PKG_PROG_PKG_CONFIG([0.22]) +GLIB_GSETTINGS +GOBJECT_INTROSPECTION_CHECK([1.42.0]) +VAPIGEN_CHECK -changequote(,)dnl -if test "x$GCC" = "xyes"; then - case " $CFLAGS " in - *[\ \ ]-Wall[\ \ ]*) ;; - *) CFLAGS="$CFLAGS -Wall" ;; - esac + +dnl *********************************************************************** +dnl Check for required packages +dnl *********************************************************************** +PKG_CHECK_MODULES(SYSPROF, [gio-2.0 >= 2.44 + gtk+-3.0 >= 3.16 + polkit-gobject-1]) +PKG_CHECK_MODULES(SYSTEMD, [libsystemd >= 222], + [have_systemd=yes], + [have_systemd=no]) + + +# we require systemd for sysprofd +AM_CONDITIONAL(ENABLE_SYSPROFD, [test x$have_systemd = xyes]) + + +# where to place systemd units if necessary +AC_ARG_WITH([systemdsystemunitdir], + AS_HELP_STRING([--with-systemdsystemunitdir=DIR], + [Directory for systemd service files]), + [], + [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) +if test "x$with_systemdsystemunitdir" != "xno"; then + AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) fi -changequote([,])dnl + +dnl *********************************************************************** +dnl Override location of debug symbols for system libraries +dnl *********************************************************************** debugdir=${libdir}/debug # Separate debug dir @@ -48,37 +127,70 @@ AC_DEFUN([AC_DEFINE_DIR], [ ]) AC_ARG_WITH(separate-debug-dir, -[ --with-separate-debug-dir=path Look for global separate debug info in this path [LIBDIR/debug]], -[debugdir="${withval}"]) + [ --with-separate-debug-dir=path Look for global separate debug info in this path [LIBDIR/debug]], + [debugdir="${withval}"]) AC_DEFINE_DIR(DEBUGDIR, debugdir, [Look for global separate debug info in this path]) -# Kernel version -KMAJOR=`uname -r | cut -d"." -f 1` -KMINOR=`uname -r | cut -d"." -f 2` -KMICRO=`uname -r | cut -d"." -f 3 | cut -d"-" -f 1` +AC_SUBST([LIBEXECDIR], `eval echo "${libexecdir}"`) -# Pkgconfig dependencies -core_dep="glib-2.0 >= 2.6.0" -gui_dep="gtk+-2.0 > 2.6.0 gdk-pixbuf-2.0 pangoft2 libglade-2.0" +dnl *********************************************************************** +dnl User documentation +dnl *********************************************************************** +YELP_HELP_INIT -PKG_CHECK_MODULES(CORE_DEP, $core_dep, [], AC_MSG_ERROR([sysprof dependencies not satisfied])) -build_gui=yes -PKG_CHECK_MODULES(GUI_DEP, $gui_dep, [], build_gui=no) +dnl *********************************************************************** +dnl Initialize Libtool +dnl *********************************************************************** +LT_PREREQ([2.2]) +LT_INIT -AM_CONDITIONAL([BUILD_GUI], [test "$build_gui" = yes]) - -# emit files - -AC_SUBST(CORE_DEP_LIBS) -AC_SUBST(GUI_DEP_LIBS) -AC_SUBST(MODULE_SUBDIR) +dnl *********************************************************************** +dnl Process .in Files +dnl *********************************************************************** AC_CONFIG_FILES([ -Makefile -]) + Makefile + lib/Makefile + lib/sysprof-version.h + + daemon/Makefile + + data/Makefile + data/icons/Makefile + data/sysprof-$API_VERSION.pc:data/sysprof.pc.in + data/org.gnome.Sysprof2.conf + data/org.gnome.sysprof2.policy + + help/Makefile + + po/Makefile.in + + src/Makefile + + tests/Makefile + + tools/Makefile +],[], +[API_VERSION='$API_VERSION']) AC_OUTPUT + + +echo "" +echo " ${PACKAGE} - ${VERSION}" +echo "" +echo " Options" +echo "" +echo " Prefix ............................... : ${prefix}" +echo " Libdir ............................... : ${libdir}" +echo " Libexecdir ........................... : ${libexecdir}" +echo "" +echo " Debug Directory ...................... : ${debugdir}" +echo "" +echo " Sysprofd ............................. : ${have_systemd}" +echo " Systemd System Units ................. : ${with_systemdsystemunitdir}" +echo "" diff --git a/daemon/Makefile.am b/daemon/Makefile.am new file mode 100644 index 00000000..c7eaaad7 --- /dev/null +++ b/daemon/Makefile.am @@ -0,0 +1,14 @@ +if ENABLE_SYSPROFD + +pkglibexec_PROGRAMS = sysprofd + +sysprofd_SOURCES = \ + sysprofd.c \ + sd-bus-helper.c \ + sd-bus-helper.h + +sysprofd_LDADD = $(SYSTEMD_LIBS) + +endif + +-include $(top_srcdir)/git.mk diff --git a/daemon/sd-bus-helper.c b/daemon/sd-bus-helper.c new file mode 100644 index 00000000..1444a073 --- /dev/null +++ b/daemon/sd-bus-helper.c @@ -0,0 +1,188 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see . +***/ + +#include +#include + +#include "sd-bus-helper.h" + +/* + * Various macros to simplify lifing code from sd-bus. + */ +#define assert_return(expr,val) do { if (!(expr)) return (val); } while (0) +#define _cleanup_(f) __attribute__((cleanup(f))) +#define STRV_FOREACH_PAIR(x, y, l) \ + for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) + +/* + * To support systemd 222, we need a couple helpers that were added + * in 229. If we update code from systemd in the future, we can probably + * drop these helpres. + */ + +static void +_sd_bus_message_unrefp (sd_bus_message **m) +{ + if (m && *m) + { + sd_bus_message_unref (*m); + *m = NULL; + } +} + +static void +_sd_bus_creds_unrefp (sd_bus_creds **c) +{ + if (c && *c) + { + sd_bus_creds_unref (*c); + *c = NULL; + } +} + +/* + * Begin verbatim code from systemd. Please try to keep this in sync until + * systemd exposes helpers for polkit integration. + */ +static int +check_good_user (sd_bus_message *m, + uid_t good_user) +{ + _cleanup_(_sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + uid_t sender_uid; + int r; + + assert (m); + + if (good_user == UID_INVALID) + return 0; + + r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + /* Don't trust augmented credentials for authorization */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM); + + r = sd_bus_creds_get_euid(creds, &sender_uid); + if (r < 0) + return r; + + return sender_uid == good_user; +} + +int +bus_test_polkit (sd_bus_message *call, + int capability, + const char *action, + const char **details, + uid_t good_user, + bool *_challenge, + sd_bus_error *e) +{ + int r; + + assert (call); + assert (action); + + /* Tests non-interactively! */ + + r = check_good_user(call, good_user); + if (r != 0) + return r; + + r = sd_bus_query_sender_privilege(call, capability); + if (r < 0) + return r; + else if (r > 0) + return 1; + else { + _cleanup_(_sd_bus_message_unrefp) sd_bus_message *request = NULL; + _cleanup_(_sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int authorized = false, challenge = false; + const char *sender, **k, **v; + + sender = sd_bus_message_get_sender(call); + if (!sender) + return -EBADMSG; + + r = sd_bus_message_new_method_call(sd_bus_message_get_bus (call), + &request, + "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", + "CheckAuthorization"); + if (r < 0) + return r; + + r = sd_bus_message_append(request, + "(sa{sv})s", + "system-bus-name", 1, "name", "s", sender, + action); + if (r < 0) + return r; + + r = sd_bus_message_open_container(request, 'a', "{ss}"); + if (r < 0) + return r; + + STRV_FOREACH_PAIR(k, v, details) { + r = sd_bus_message_append(request, "{ss}", *k, *v); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(request); + if (r < 0) + return r; + + r = sd_bus_message_append(request, "us", 0, NULL); + if (r < 0) + return r; + + r = sd_bus_call(sd_bus_message_get_bus(call), request, 0, e, &reply); + if (r < 0) { + /* Treat no PK available as access denied */ + if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) { + sd_bus_error_free(e); + return -EACCES; + } + + return r; + } + + r = sd_bus_message_enter_container(reply, 'r', "bba{ss}"); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "bb", &authorized, &challenge); + if (r < 0) + return r; + + if (authorized) + return 1; + + if (_challenge) { + *_challenge = challenge; + return 0; + } + } + + return -EACCES; +} diff --git a/daemon/sd-bus-helper.h b/daemon/sd-bus-helper.h new file mode 100644 index 00000000..58e2b9b8 --- /dev/null +++ b/daemon/sd-bus-helper.h @@ -0,0 +1,36 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see . +***/ + +#ifndef SD_BUS_HELPER_H +#define SD_BUS_HELPER_H + +#include +#include + +#define UID_INVALID ((uid_t)-1) + +int bus_test_polkit (sd_bus_message *call, + int capability, + const char *action, + const char **details, + uid_t good_user, + bool *_challenge, + sd_bus_error *e); + +#endif /* SD_BUS_HELPER_H */ diff --git a/daemon/sysprofd.c b/daemon/sysprofd.c new file mode 100644 index 00000000..17004561 --- /dev/null +++ b/daemon/sysprofd.c @@ -0,0 +1,347 @@ +/* sysprofd.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "sd-bus-helper.h" + +#define BUS_TIMEOUT_USEC (1000000L * 10L) + +static int +_perf_event_open (struct perf_event_attr *attr, + pid_t pid, + int cpu, + int group_fd, + unsigned long flags) +{ + assert (attr != NULL); + + /* Quick sanity check */ + if (attr->sample_period < 100000) + return -EINVAL; + + return syscall (__NR_perf_event_open, attr, pid, cpu, group_fd, flags); +} + +#if 0 +#define GOTO(l) do { \ + fprintf (stderr, "GOTO: %s:%d: " #l "\n", __FUNCTION__, __LINE__); \ + goto l; \ +} while (0) +#else +#define GOTO(l) goto l +#endif + +static int +sysprofd_perf_event_open (sd_bus_message *msg, + void *user_data, + sd_bus_error *error) +{ + struct perf_event_attr attr = { 0 }; + sd_bus_message *reply = NULL; + sd_bus_message *kvpair; + uint64_t flags = 0; + int disabled = 0; + int32_t wakeup_events = 149; + int32_t cpu = -1; + int32_t pid = -1; + bool challenge = false; + int32_t type = 0; + uint64_t sample_period = 0; + uint64_t sample_type = 0; + uint64_t config = 0; + int clockid = CLOCK_MONOTONIC_RAW; + int comm = 0; + int mmap_ = 0; + int task = 0; + int exclude_idle = 0; + int fd = -1; + int use_clockid = 0; + int r; + + assert (msg); + + r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return r; + + for (;;) + { + const char *name = NULL; + + r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_DICT_ENTRY, "sv"); + if (r < 0) + return r; + + if (r == 0) + break; + + r = sd_bus_message_read (msg, "s", &name); + if (r < 0) + goto cleanup; + + r = sd_bus_message_enter_container (msg, SD_BUS_TYPE_VARIANT, NULL); + if (r < 0) + goto cleanup; + + if (strcmp (name, "disabled") == 0) + { + r = sd_bus_message_read (msg, "b", &disabled); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "wakeup_events") == 0) + { + r = sd_bus_message_read (msg, "u", &wakeup_events); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "clockid") == 0) + { + r = sd_bus_message_read (msg, "i", &clockid); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "comm") == 0) + { + r = sd_bus_message_read (msg, "b", &comm); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "exclude_idle") == 0) + { + r = sd_bus_message_read (msg, "b", &exclude_idle); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "mmap") == 0) + { + r = sd_bus_message_read (msg, "b", &mmap_); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "config") == 0) + { + r = sd_bus_message_read (msg, "t", &config); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "sample_period") == 0) + { + r = sd_bus_message_read (msg, "t", &sample_period); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "sample_type") == 0) + { + r = sd_bus_message_read (msg, "t", &sample_type); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "task") == 0) + { + r = sd_bus_message_read (msg, "b", &task); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "type") == 0) + { + r = sd_bus_message_read (msg, "u", &type); + if (r < 0) + GOTO (cleanup); + } + else if (strcmp (name, "use_clockid") == 0) + { + r = sd_bus_message_read (msg, "b", &use_clockid); + if (r < 0) + GOTO (cleanup); + } + + r = sd_bus_message_exit_container (msg); + if (r < 0) + goto cleanup; + + sd_bus_message_exit_container (msg); + if (r < 0) + goto cleanup; + + cleanup: + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container (msg); + if (r < 0) + return r; + + r = sd_bus_message_read (msg, "iit", &pid, &cpu, &flags); + if (r < 0) + return r; + + if (pid < -1 || cpu < -1) + return -EINVAL; + + r = sd_bus_message_new_method_return (msg, &reply); + if (r < 0) + return r; + + /* Authorize peer */ + r = bus_test_polkit (msg, + CAP_SYS_ADMIN, + "org.gnome.sysprof2.perf-event-open", + NULL, + UID_INVALID, + &challenge, + error); + if (r < 0) + return r; + else if (r == 0) + return -EACCES; + + if (!use_clockid || clockid < 0) + clockid = CLOCK_MONOTONIC_RAW; + + attr.comm = !!comm; + attr.config = config; + attr.disabled = disabled; + attr.exclude_idle = !!exclude_idle; + attr.mmap = !!mmap_; + attr.sample_period = sample_period; + attr.sample_type = sample_type; + attr.size = sizeof attr; + attr.task = !!task; + attr.type = type; + attr.clockid = clockid; + attr.use_clockid = use_clockid; + attr.wakeup_events = wakeup_events; + + fd = _perf_event_open (&attr, pid, cpu, -1, 0); + if (fd < 0) + { + fprintf (stderr, + "Failed to open perf event stream: %s\n", + strerror (errno)); + return -EINVAL; + } + + sd_bus_message_append_basic (reply, SD_BUS_TYPE_UNIX_FD, &fd); + r = sd_bus_send (NULL, reply, NULL); + sd_bus_message_unref (reply); + + close (fd); + + return r; +} + +static const sd_bus_vtable sysprofd_vtable[] = { + SD_BUS_VTABLE_START (0), + SD_BUS_METHOD ("PerfEventOpen", "a{sv}iit", "h", sysprofd_perf_event_open, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +int +main (int argc, + char *argv[]) +{ + sd_bus_slot *slot = NULL; + sd_bus *bus = NULL; + int r; + + /* Connect to the system bus */ + r = sd_bus_default_system (&bus); + if (r < 0) + { + fprintf (stderr, + "Failed to connect to system bus: %s\n", + strerror (-r)); + goto failure; + } + + /* Install our object */ + r = sd_bus_add_object_vtable (bus, + &slot, + "/org/gnome/Sysprof2", + "org.gnome.Sysprof2", + sysprofd_vtable, + NULL); + if (r < 0) + { + fprintf (stderr, + "Failed to install object on bus: %s\n", + strerror (-r)); + goto failure; + } + + /* Request our well-known name on the bus */ + r = sd_bus_request_name (bus, "org.gnome.Sysprof2", 0); + if (r < 0) + { + fprintf (stderr, + "Failed to register name on the bus: %s\n", + strerror (-r)); + goto failure; + } + + for (;;) + { + /* Process requests */ + r = sd_bus_process (bus, NULL); + if (r < 0) + { + fprintf (stderr, + "Failed to process bus: %s\n", + strerror (-r)); + goto failure; + } + + /* If we processed a request, continue processing */ + if (r > 0) + continue; + + /* Wait for the next request to process */ + r = sd_bus_wait (bus, BUS_TIMEOUT_USEC); + if (r < 0) + { + fprintf (stderr, + "Failed to wait on bus: %s\n", + strerror (-r)); + goto failure; + } + + /* + * If we timed out, we can expire, we will be auto-started by + * systemd or dbus on the next activation request. + */ + if (r == 0) + break; + } + +failure: + sd_bus_slot_unref (slot); + sd_bus_unref (bus); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 00000000..d76e65cf --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,86 @@ +SUBDIRS = icons + +mimedir = $(datadir)/mime/packages +mime_DATA = sysprof-mime.xml + +desktopdir = $(datadir)/applications +desktop_DATA = org.gnome.Sysprof2.desktop + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = sysprof-$(API_VERSION).pc + +gsettings_SCHEMAS = org.gnome.sysprof2.gschema.xml + +.PRECIOUS: $(gsettings_SCHEMAS) + +@GSETTINGS_RULES@ + +EXTRA_DIST = \ + sysprof.pc.in \ + org.gnome.Sysprof2.desktop \ + $(mime_DATA) \ + $(gsettings_SCHEMAS) \ + $(NULL) + +DISTCLEANFILES = $(pkgconfig_DATA) +GITIGNOREFILES = $(dbusservice_DATA) + + +if ENABLE_SYSPROFD + +dbusservicedir = $(datadir)/dbus-1/system-services +dbusservice_in_files = org.gnome.Sysprof2.service.in +dbusservice_DATA = $(dbusservice_in_files:.service.in=.service) + +$(dbusservice_DATA): $(dbusservice_in_files) Makefile + @sed -e "s|\@sysprofdprivdir\@|$(libexecdir)/$(PACKAGE)|" $< > $@ + +dbusconfdir = $(datadir)/dbus-1/system.d +dbusconf_in_files = org.gnome.Sysprof2.conf.in +dbusconf_DATA = $(dbusconf_in_files:.conf.in=.conf) + +systemdservice_in_files = sysprof2.service.in +systemdservicedir = $(systemdsystemunitdir) +systemdservice_DATA = $(systemdservice_in_files:.service.in=.service) + +$(systemdservice_DATA): $(systemdservice_in_files) Makefile + @sed -e "s|\@sysprofdprivdir\@|$(libexecdir)/$(PACKAGE)|" $< > $@ + +polkitdir = $(datadir)/polkit-1/actions +polkit_in_files = org.gnome.sysprof2.policy.in +polkit_DATA = $(polkit_in_files:.policy.in=.policy) + +DISTCLEANFILES += \ + $(systemdservice_DATA) \ + $(dbusservice_DATA) \ + $(NULL) + +EXTRA_DIST += \ + org.gnome.Sysprof2.xml \ + $(dbusservice_in_files) \ + $(dbusconf_in_files) \ + $(systemdservice_in_files) \ + $(polkit_in_files) \ + $(NULL) + +GITIGNOREFILES += $(systemdservice_DATA) + +endif + + +install-data-local: install-mimeDATA + if [ -f $(DESTDIR)$(datadir)/mime/packages/freedesktop.org.xml ] ; then \ + if which update-mime-database>/dev/null 2>&1; then \ + update-mime-database $(DESTDIR)$(datadir)/mime; \ + fi \ + fi + +uninstall-local: + if [ -f $(DESTDIR)$(datadir)/mime/packages/freedesktop.org.xml ] ; then \ + if which update-mime-database>/dev/null 2>&1; then \ + update-mime-database $(DESTDIR)$(datadir)/mime; \ + fi \ + fi + + +-include $(top_srcdir)/git.mk diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am new file mode 100644 index 00000000..e4674db1 --- /dev/null +++ b/data/icons/Makefile.am @@ -0,0 +1,29 @@ +icondir = $(datadir)/icons/hicolor + +nobase_icon_DATA = \ + 16x16/apps/sysprof.png \ + 24x24/apps/sysprof.png \ + 32x32/apps/sysprof.png \ + 48x48/apps/sysprof.png \ + 256x256/apps/sysprof.png \ + scalable/apps/sysprof-symbolic.svg \ + $(NULL) + +EXTRA_DIST = \ + $(nobase_icon_DATA) \ + sysprof-source.svg + +gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor + +install-data-hook: update-icon-cache +uninstall-hook: update-icon-cache +update-icon-cache: + @-if test -z "$(DESTDIR)"; then \ + echo "Updating Gtk icon cache."; \ + $(gtk_update_icon_cache); \ + else \ + echo "*** Icon cache not updated. After (un)install, run this:"; \ + echo "*** $(gtk_update_icon_cache)"; \ + fi + +-include $(top_srcdir)/git.mk diff --git a/data/org.gnome.Sysprof2.conf.in b/data/org.gnome.Sysprof2.conf.in new file mode 100644 index 00000000..ec693bbf --- /dev/null +++ b/data/org.gnome.Sysprof2.conf.in @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/data/org.gnome.Sysprof2.desktop b/data/org.gnome.Sysprof2.desktop new file mode 100644 index 00000000..7dc0a20d --- /dev/null +++ b/data/org.gnome.Sysprof2.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Version=1.0 +Name=Sysprof +GenericName=Profiler +Comment=Profile an application or entire system. +Exec=sysprof %u +TryExec=sysprof +Icon=sysprof +StartupNotify=true +Terminal=false +Type=Application +Categories=GNOME;Development; +MimeType=application/x-sysprof-capture; diff --git a/data/org.gnome.Sysprof2.service.in b/data/org.gnome.Sysprof2.service.in new file mode 100644 index 00000000..3455b650 --- /dev/null +++ b/data/org.gnome.Sysprof2.service.in @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=org.gnome.Sysprof2 +Exec=@sysprofdprivdir@/sysprofd +User=root +SystemdService=sysprof2.service diff --git a/data/org.gnome.Sysprof2.xml b/data/org.gnome.Sysprof2.xml new file mode 100644 index 00000000..46e87e12 --- /dev/null +++ b/data/org.gnome.Sysprof2.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/data/org.gnome.sysprof2.gschema.xml b/data/org.gnome.sysprof2.gschema.xml new file mode 100644 index 00000000..8e9dc429 --- /dev/null +++ b/data/org.gnome.sysprof2.gschema.xml @@ -0,0 +1,34 @@ + + + + (-1, -1) + Window size + Window size (width and height). + + + (-1,-1) + Window position + Window position (x and y). + + + true + Window maximized + Window maximized state + + + '' + Last Spawn Program + The last spawned program, which will be set in the UI upon restart of the application. + + + true + Last Spawn Inherit Environment + If the last spawned environment inherits the parent environment. + + + [] + Last Spawn Environment + The last spawned environment, which will be set in the UI upon restart of the application. + + + diff --git a/data/org.gnome.sysprof2.policy.in b/data/org.gnome.sysprof2.policy.in new file mode 100644 index 00000000..5ba36e11 --- /dev/null +++ b/data/org.gnome.sysprof2.policy.in @@ -0,0 +1,22 @@ + + + + + + The sysprof Project + https://wiki.gnome.org/Apps/Sysprof + sysprof + + + Open a perf event stream + Authentication is required to access system performance counters. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/data/sysprof-mime.xml b/data/sysprof-mime.xml new file mode 100644 index 00000000..5f7e2d95 --- /dev/null +++ b/data/sysprof-mime.xml @@ -0,0 +1,7 @@ + + + + Sysprof Capture + + + diff --git a/data/sysprof.pc.in b/data/sysprof.pc.in new file mode 100644 index 00000000..bfdb998f --- /dev/null +++ b/data/sysprof.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${exec_prefix}/include + +Name: libsysprof-@API_VERSION@ +Description: The sysprof library for integrating profiling into IDEs +Version: @VERSION@ +Libs: -L${libdir} -lsysprof-@API_VERSION@ +Cflags: -I${includedir}/sysprof-@API_VERSION@ +Requires: gio-2.0 gtk+-3.0 diff --git a/data/sysprof2.service.in b/data/sysprof2.service.in new file mode 100644 index 00000000..9ee46328 --- /dev/null +++ b/data/sysprof2.service.in @@ -0,0 +1,8 @@ +[Unit] +Description=Sysprof Daemon + +[Service] +Type=dbus +BusName=org.gnome.Sysprof2 +ExecStart=@sysprofdprivdir@/sysprofd + diff --git a/git.mk b/git.mk new file mode 100644 index 00000000..9d4bf251 --- /dev/null +++ b/git.mk @@ -0,0 +1,333 @@ +# git.mk, a small Makefile to autogenerate .gitignore files +# for autotools-based projects. +# +# Copyright 2009, Red Hat, Inc. +# Copyright 2010,2011,2012,2013 Behdad Esfahbod +# Written by Behdad Esfahbod +# +# Copying and distribution of this file, with or without modification, +# is permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# +# The latest version of this file can be downloaded from: +GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk +# +# Bugs, etc, should be reported upstream at: +# https://github.com/behdad/git.mk +# +# To use in your project, import this file in your git repo's toplevel, +# then do "make -f git.mk". This modifies all Makefile.am files in +# your project to -include git.mk. Remember to add that line to new +# Makefile.am files you create in your project, or just rerun the +# "make -f git.mk". +# +# This enables automatic .gitignore generation. If you need to ignore +# more files, add them to the GITIGNOREFILES variable in your Makefile.am. +# But think twice before doing that. If a file has to be in .gitignore, +# chances are very high that it's a generated file and should be in one +# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. +# +# The only case that you need to manually add a file to GITIGNOREFILES is +# when remove files in one of mostlyclean-local, clean-local, distclean-local, +# or maintainer-clean-local make targets. +# +# Note that for files like editor backup, etc, there are better places to +# ignore them. See "man gitignore". +# +# If "make maintainer-clean" removes the files but they are not recognized +# by this script (that is, if "git status" shows untracked files still), send +# me the output of "git status" as well as your Makefile.am and Makefile for +# the directories involved and I'll diagnose. +# +# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see +# Makefile.am.sample in the git.mk git repo. +# +# Don't EXTRA_DIST this file. It is supposed to only live in git clones, +# not tarballs. It serves no useful purpose in tarballs and clutters the +# build dir. +# +# This file knows how to handle autoconf, automake, libtool, gtk-doc, +# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata, +# appstream. +# +# This makefile provides the following targets: +# +# - all: "make all" will build all gitignore files. +# - gitignore: makes all gitignore files in the current dir and subdirs. +# - .gitignore: make gitignore file for the current dir. +# - gitignore-recurse: makes all gitignore files in the subdirs. +# +# KNOWN ISSUES: +# +# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the +# submodule doesn't find us. If you have configure.{in,ac} files in +# subdirs, add a proxy git.mk file in those dirs that simply does: +# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. +# And add those files to git. See vte/gnome-pty-helper/git.mk for +# example. +# + + + +############################################################################### +# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES: +############################################################################### + +# +# Most autotools-using modules should be fine including this variable in their +# toplevel MAINTAINERCLEANFILES: +GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \ + $(srcdir)/aclocal.m4 \ + $(srcdir)/autoscan.log \ + $(srcdir)/configure.scan \ + `AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \ + test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \ + for x in \ + ar-lib \ + compile \ + config.guess \ + config.sub \ + depcomp \ + install-sh \ + ltmain.sh \ + missing \ + mkinstalldirs \ + test-driver \ + ylwrap \ + ; do echo "$$AUX_DIR/$$x"; done` \ + `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \ + head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done` +# +# All modules should also be fine including the following variable, which +# removes automake-generated Makefile.in files: +GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \ + `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \ + while read f; do \ + case $$f in Makefile|*/Makefile) \ + test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \ + done` +# +# Modules that use libtool and use AC_CONFIG_MACRO_DIR() may also include this, +# though it's harmless to include regardless. +GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \ + `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ + if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ + for x in \ + libtool.m4 \ + ltoptions.m4 \ + ltsugar.m4 \ + ltversion.m4 \ + lt~obsolete.m4 \ + ; do echo "$$MACRO_DIR/$$x"; done; \ + fi` + + + +############################################################################### +# Default rule is to install ourselves in all Makefile.am files: +############################################################################### + +git-all: git-mk-install + +git-mk-install: + @echo "Installing git makefile" + @any_failed=; \ + find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \ + if grep 'include .*/git.mk' $$x >/dev/null; then \ + echo "$$x already includes git.mk"; \ + else \ + failed=; \ + echo "Updating $$x"; \ + { cat $$x; \ + echo ''; \ + echo '-include $$(top_srcdir)/git.mk'; \ + } > $$x.tmp || failed=1; \ + if test x$$failed = x; then \ + mv $$x.tmp $$x || failed=1; \ + fi; \ + if test x$$failed = x; then : else \ + echo "Failed updating $$x"; >&2 \ + any_failed=1; \ + fi; \ + fi; done; test -z "$$any_failed" + +git-mk-update: + wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk + +.PHONY: git-all git-mk-install git-mk-update + + + +############################################################################### +# Actual .gitignore generation: +############################################################################### + +$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk + @echo "git.mk: Generating $@" + @{ \ + if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ + for x in \ + $(DOC_MODULE)-decl-list.txt \ + $(DOC_MODULE)-decl.txt \ + tmpl/$(DOC_MODULE)-unused.sgml \ + "tmpl/*.bak" \ + xml html \ + ; do echo "/$$x"; done; \ + FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \ + case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \ + fi; \ + if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ + for lc in $(DOC_LINGUAS); do \ + for x in \ + $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ + $(DOC_PAGES) \ + $(DOC_INCLUDES) \ + ; do echo "/$$lc/$$x"; done; \ + done; \ + for x in \ + $(_DOC_OMF_ALL) \ + $(_DOC_DSK_ALL) \ + $(_DOC_HTML_ALL) \ + $(_DOC_MOFILES) \ + $(DOC_H_FILE) \ + "*/.xml2po.mo" \ + "*/*.omf.out" \ + ; do echo /$$x; done; \ + fi; \ + if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ + for lc in $(HELP_LINGUAS); do \ + for x in \ + $(HELP_FILES) \ + "$$lc.stamp" \ + "$$lc.mo" \ + ; do echo "/$$lc/$$x"; done; \ + done; \ + fi; \ + if test "x$(gsettings_SCHEMAS)" = x; then :; else \ + for x in \ + $(gsettings_SCHEMAS:.xml=.valid) \ + $(gsettings__enum_file) \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(appdata_XML)" = x; then :; else \ + for x in \ + $(appdata_XML:.xml=.valid) \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(appstream_XML)" = x; then :; else \ + for x in \ + $(appstream_XML:.xml=.valid) \ + ; do echo "/$$x"; done; \ + fi; \ + if test -f $(srcdir)/po/Makefile.in.in; then \ + for x in \ + po/Makefile.in.in \ + po/Makefile.in.in~ \ + po/Makefile.in \ + po/Makefile \ + po/Makevars.template \ + po/POTFILES \ + po/Rules-quot \ + po/stamp-it \ + po/.intltool-merge-cache \ + "po/*.gmo" \ + "po/*.header" \ + "po/*.mo" \ + "po/*.sed" \ + "po/*.sin" \ + po/$(GETTEXT_PACKAGE).pot \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in \ + ; do echo "/$$x"; done; \ + fi; \ + if test -f $(srcdir)/configure; then \ + for x in \ + autom4te.cache \ + configure \ + config.h \ + stamp-h1 \ + libtool \ + config.lt \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(DEJATOOL)" = x; then :; else \ + for x in \ + $(DEJATOOL) \ + ; do echo "/$$x.sum"; echo "/$$x.log"; done; \ + echo /site.exp; \ + fi; \ + if test "x$(am__dirstamp)" = x; then :; else \ + echo "$(am__dirstamp)"; \ + fi; \ + if test "x$(LTCOMPILE)" = x -a "x$(LTCXXCOMPILE)" = x -a "x$(GTKDOC_RUN)" = x; then :; else \ + for x in \ + "*.lo" \ + ".libs" "_libs" \ + ; do echo "$$x"; done; \ + fi; \ + for x in \ + .gitignore \ + $(GITIGNOREFILES) \ + $(CLEANFILES) \ + $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ + $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ + $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ + so_locations \ + $(MOSTLYCLEANFILES) \ + $(TEST_LOGS) \ + $(TEST_LOGS:.log=.trs) \ + $(TEST_SUITE_LOG) \ + $(TESTS:=.test) \ + "*.gcda" \ + "*.gcno" \ + $(DISTCLEANFILES) \ + $(am__CONFIG_DISTCLEAN_FILES) \ + $(CONFIG_CLEAN_FILES) \ + TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ + "*.tab.c" \ + $(MAINTAINERCLEANFILES) \ + $(BUILT_SOURCES) \ + $(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \ + $(filter %_vala.stamp,$(DIST_COMMON)) \ + $(filter %.vapi,$(DIST_COMMON)) \ + $(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \ + Makefile \ + Makefile.in \ + "*.orig" \ + "*.rej" \ + "*.bak" \ + "*~" \ + ".*.sw[nop]" \ + ".dirstamp" \ + ; do echo "/$$x"; done; \ + for x in \ + "*.$(OBJEXT)" \ + $(DEPDIR) \ + ; do echo "$$x"; done; \ + } | \ + sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ + sed 's@/[.]/@/@g' | \ + LC_ALL=C sort | uniq > $@.tmp && \ + mv $@.tmp $@; + +all: $(srcdir)/.gitignore gitignore-recurse-maybe +gitignore: $(srcdir)/.gitignore gitignore-recurse + +gitignore-recurse-maybe: + @for subdir in $(DIST_SUBDIRS); do \ + case " $(SUBDIRS) " in \ + *" $$subdir "*) :;; \ + *) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \ + esac; \ + done +gitignore-recurse: + @for subdir in $(DIST_SUBDIRS); do \ + test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \ + done + +maintainer-clean: gitignore-clean +gitignore-clean: + -rm -f $(srcdir)/.gitignore + +.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe diff --git a/gresources.mk b/gresources.mk new file mode 100644 index 00000000..d1b849bc --- /dev/null +++ b/gresources.mk @@ -0,0 +1,62 @@ +# Rules for generating gresources using glib-compile-resources +# +# Define: +# glib_resources_h = header template file +# glib_resources_c = source template file +# glib_resources_xml = path to *.gresource.xml +# glib_resources_namespace = c prefix for resources +# +# before including Makefile.am.resources. You will also need to have +# the following targets already defined: +# +# CLEANFILES +# DISTCLEANFILES +# BUILT_SOURCES +# EXTRA_DIST +# +# Author: Christian Hergert + +# Basic sanity checks +$(if $(GLIB_COMPILE_RESOURCES),,$(error Need to define GLIB_COMPILE_RESOURCES)) + +$(if $(or $(glib_resources_h), \ + $(glib_resources_c)),, \ + $(error Need to define glib_resources_h and glib_resources_c)) + +$(if $(glib_resources_xml),,$(error Need to define glib_resources_xml)) +$(if $(glib_resources_namespace),,$(error Need to define glib_resources_namespace)) + +resources_xml=$(addprefix $(srcdir)/,$(glib_resources_xml)) +resources_srcdir=$(dir $(resources_xml)) + +DISTCLEANFILES += $(glib_resources_h) $(glib_resources_c) +BUILT_SOURCES += $(glib_resources_h) $(glib_resources_c) +CLEANFILES += stamp-resources $(glib_resources_c) $(glib_resources_h) +EXTRA_DIST += \ + $(glib_resources_xml) \ + $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(resources_srcdir) --generate-dependencies $(resources_xml)) \ + $(NULL) + +stamp-resources: $(glib_resources_c) $(resources_xml) + $(AM_V_GEN)$(GLIB_COMPILE_RESOURCES) \ + --target=xgen-gr.h \ + --sourcedir=$(resources_srcdir) \ + --generate-header \ + --c-name $(glib_resources_namespace) \ + $(resources_xml) \ + && (cmp -s xgen-gr.h $(glib_resources_h) || cp -f xgen-gr.h $(glib_resources_h)) \ + && rm -f xgen-gr.h \ + && echo timestamp > $(@F) + +$(glib_resources_h): stamp-resources + @true + +$(glib_resources_c): $(resources_xml) $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(resources_srcdir) --generate-dependencies $(resources_xml)) + $(AM_V_GEN)$(GLIB_COMPILE_RESOURCES) \ + --target=xgen-gr.c \ + --sourcedir=$(resources_srcdir) \ + --generate-source \ + --c-name $(glib_resources_namespace) \ + $(resources_xml) \ + && (cmp -s xgen-gr.c $(glib_resources_c) || cp -f xgen-gr.c $(glib_resources_c)) \ + && rm -f xgen-gr.c diff --git a/help/C/index.page b/help/C/index.page new file mode 100644 index 00000000..56596f15 --- /dev/null +++ b/help/C/index.page @@ -0,0 +1,18 @@ + + + + + + + + + Sysprof + +
+ Profiling +
+ +
diff --git a/help/C/introduction.page b/help/C/introduction.page new file mode 100644 index 00000000..3cf93b18 --- /dev/null +++ b/help/C/introduction.page @@ -0,0 +1,57 @@ + + + + + + + + + Christian Hergert + christian@hergert.me + 2016 + + + + + Welcome to Sysprof! + + + Introduction + +

Sysprof is a system profiler for Linux that targets + the GNOME desktop.

+ +
+ + + Differences between tracing and sampling + + What is a Profiler? + +

A profiler is an application that records information about an +application or system while it runs. That information can be explored to +gain insight into how the application could be changed to perform +better.

+ +

Two common categories of software profilers exist, commonly referred +to as either tracing or sampling profilers. What is meant by tracing +profiler is that every function call executed by the program is known to the +profiler. A sampling profiler works by inspecting the state of the +program on a regular frequency and therefore does not see every function +call executed by the program.

+ +

Both tracing and sampling profilers have their advantages. A +notable advtantage of a sampling profiler is that the overhead is much +less than that of a tracing profiler, making it easier to use for +software that requires interactivity.

+ +

Sysprof is a sampling profiler.

+
+ +
diff --git a/help/C/legal.xml b/help/C/legal.xml new file mode 100644 index 00000000..42fd3db3 --- /dev/null +++ b/help/C/legal.xml @@ -0,0 +1,12 @@ + + +

This work is licensed under a + Creative Commons + Attribution-ShareAlike 4.0 International license.

+ +

As a special exception, the copyright holders give you permission to copy, + modify, and distribute the example code contained in this documentation under + the terms of your choosing, without restriction.

+ +
diff --git a/help/C/profiling.page b/help/C/profiling.page new file mode 100644 index 00000000..4b0a666f --- /dev/null +++ b/help/C/profiling.page @@ -0,0 +1,140 @@ + + +
+ + + + How to profile your system +

When Sysprof profiles your system, it records stack +information for all applications executing, including the Linux kernel. This +can sometimes be confusing if you only want to look at a single process. If +your application does not interact much with the host system, you may have more +success with profiling an existing +process.

+ +

To profile your entire system, ensure the target button is set to +All Processes and click Record.

+ +

At this point, you may be asked to authorize access to +profile the system. This is required as the Linux kernel's perf +implementation requires root to perform whole-system profiling.

+ +

During the profiling session, you will see the number of +seconds the profile has been active. Clicking the Record +button again will stop the profiling session. Afterwhich, the callgraph +will be displayed.

+ + +

If you find that the sysprof application is showing up in +your profiling callgraph, you might consider recording the profiling session +with sysprof-cli. This is a command line program that will capture +your profiling session to disk to be viewed at a later time.

+
+ +

See interpreting +results for more guidance.

+ +
+ +
+ + + + Profile an existing process +

With Sysprof, you can profile one or more existing +processes on your system. First, select the profiling target +button next to the Record button. Select Existing Process +in the popover that is displayed. Next, select as many processes as you'd +like to profile. Processes selected for profiling will have a checkmark +next to their name.

+ +

After selecting your target processes, click the Record +button to start profiling.

+ +

When you have completed, click the Record button again +to stop profiling.

+ +

See interpreting +results for more guidance.

+ +
+ +
+ + + + Profile a new process + +

Often times, you may need to spawn a new process to profile. +First, select the profiling target button next to the +Record button. Next, select New Process and fill +out the necessary information to spawn the process.

+ + +

If you are spawning a process that requires access to your current +display, such as a GTK+ application, you will want to make sure Inherit +current environment is set.

+
+ +
+ +
+ + + + Profiling with the sysprof-cli command line tool + +

For minimal overhead, you might consider using the sysprof-cli +command line tool. When run without any arguments, it will record your entire +system and save the output to capture.syscap. This file can be +opened with the Sysprof application to view the callgraph.

+ +

You can also attach to an existing process using +sysprof-cli -p pid.

+ +

If you would like to spawn a new process, use sysprof-cli -c +'command' to specify a command to be launched. The command will inherit +the current environment.

+ +
+ +
+ + + + Interpreting results + +

The profiling results in Sysprof are split into three +sections. On the top left is a list of all the functions profiled. They +are sorted by how often they were called during the sampling frequency.

+ + +

It is important to note that the amount of time spent in each function +is not captured. That would require a tracing profiler to accurately record. +The percentage is calculated by determining how often that function showed +up in the current stacktrace when a sample was recorded.

+
+ +

After selecting a function from the functions list, all of the recorded +callers of that function will be displayed on the bottom left. They are also +sorted by the percentage of samples that included that function in the +stacktrace.

+ +

On the right, are all of the decendants of a selected function. You can +select a function either from the functions list, or the callers list.

+ +

You can jump into a function by activating a row in the tree of descendants +with a double-click or by pressing Enter or Spacebar.

+ + +

If you see - - kernel - - in your results, that means that the +application transitioned into or from the Linux kernel. There can be many reasons +for this such as a syscall or signal.

+
+ +
+ +
diff --git a/help/Makefile.am b/help/Makefile.am new file mode 100644 index 00000000..292360ec --- /dev/null +++ b/help/Makefile.am @@ -0,0 +1,18 @@ +@YELP_HELP_RULES@ + +HELP_ID = sysprof + +# Media files +HELP_MEDIA = + +# Help pages +HELP_FILES = \ + index.page \ + introduction.page \ + profiling.page \ + legal.xml + +# Translated languages +HELP_LINGUAS = + +-include $(top_srcdir)/git.mk diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 00000000..ef782571 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,128 @@ +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +BUILT_SOURCES = + +lib_LTLIBRARIES = libsysprof-@API_VERSION@.la + +nodist_libsysprof_@API_VERSION@_la_SOURCES = \ + sp-resources.c \ + sp-resources.h + +headersdir = $(includedir)/sysprof-@API_VERSION@ +headers_DATA = \ + sysprof.h \ + sysprof-version.h \ + sp-address.h \ + sp-callgraph-profile.h \ + sp-callgraph-view.h \ + sp-capture-reader.h \ + sp-capture-writer.h \ + sp-capture-types.h \ + sp-cell-renderer-percent.h \ + sp-clock.h \ + sp-elf-symbol-resolver.h \ + sp-empty-state-view.h \ + sp-failed-state-view.h \ + sp-error.h \ + sp-gjs-source.h \ + sp-jitmap-symbol-resolver.h \ + sp-kernel-symbol.h \ + sp-kernel-symbol-resolver.h \ + sp-map-lookaside.h \ + sp-model-filter.h \ + sp-perf-source.h \ + sp-proc-source.h \ + sp-process-model.h \ + sp-process-model-item.h \ + sp-process-model-row.h \ + sp-profile.h \ + sp-profiler.h \ + sp-profiler-menu-button.h \ + sp-recording-state-view.h \ + sp-source.h \ + sp-symbol-resolver.h \ + $(NULL) + +libsysprof_@API_VERSION@_la_SOURCES = \ + $(headers_DATA) \ + sp-address.c \ + sp-callgraph-profile.c \ + sp-callgraph-profile-private.h \ + sp-callgraph-view.c \ + sp-capture-reader.c \ + sp-capture-writer.c \ + sp-cell-renderer-percent.c \ + sp-clock.c \ + sp-elf-symbol-resolver.c \ + sp-empty-state-view.c \ + sp-failed-state-view.c \ + sp-error.c \ + sp-gjs-source.c \ + sp-jitmap-symbol-resolver.c \ + sp-kernel-symbol.c \ + sp-kernel-symbol-resolver.c \ + sp-line-reader.c \ + sp-line-reader.h \ + sp-map-lookaside.c \ + sp-model-filter.c \ + sp-perf-counter.c \ + sp-perf-counter.h \ + sp-perf-source.c \ + sp-proc-source.c \ + sp-process-model.c \ + sp-process-model-item.c \ + sp-process-model-row.c \ + sp-profile.c \ + sp-profiler.c \ + sp-profiler-menu-button.c \ + sp-recording-state-view.c \ + sp-scrolled-window.c \ + sp-scrolled-window.h \ + sp-source.c \ + sp-symbol-resolver.c \ + util/binfile.c \ + util/binfile.h \ + util/demangle.cpp \ + util/demangle.h \ + util/elfparser.c \ + util/elfparser.h \ + util/stackstash.c \ + util/stackstash.h \ + util/util.h \ + $(NULL) + + +libsysprof_@API_VERSION@_la_CFLAGS = \ + -I$(srcdir)/util \ + $(SYSPROF_CFLAGS) \ + $(WARN_CFLAGS) \ + $(NULL) + +libsysprof_@API_VERSION@_la_CXXFLAGS = \ + -I$(srcdir)/util \ + $(SYSPROF_CFLAGS) \ + $(WARN_CXXFLAGS) \ + $(NULL) + +if ENABLE_SYSPROFD +libsysprof_@API_VERSION@_la_CFLAGS += -DENABLE_SYSPROFD +endif + +libsysprof_@API_VERSION@_la_LIBADD = \ + $(SYSPROF_LIBS) \ + $(NULL) + +libsysprof_@API_VERSION@_la_LDFLAGS = \ + $(WARN_LDFLAGS) \ + $(NULL) + + +glib_resources_xml = resources/libsysprof.gresource.xml +glib_resources_c = sp-resources.c +glib_resources_h = sp-resources.h +glib_resources_namespace = sp +include $(top_srcdir)/gresources.mk + + +-include $(top_srcdir)/git.mk diff --git a/lib/binfile.c b/lib/binfile.c deleted file mode 100644 index 8a2cfed1..00000000 --- a/lib/binfile.c +++ /dev/null @@ -1,530 +0,0 @@ -/* MemProf -- memory profiler and leak detector - * Copyright 1999, 2000, 2001, Red Hat, Inc. - * Copyright 2002, Kristian Rietveld - * - * Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, 2005, 2006, 2007, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* Most interesting code in this file is lifted from bfdutils.c - * and process.c from Memprof, - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "binfile.h" -#include "elfparser.h" -#include "util.h" - -struct bin_file_t -{ - int ref_count; - - GList * elf_files; - - char * filename; - - char * undefined_name; - - gulong text_offset; - - gboolean inode_check; - ino_t inode; -}; - -static ino_t -read_inode (const char *filename) -{ - struct stat statbuf; - - if (strcmp (filename, "[vdso]") == 0) - return (ino_t)0; - - if (stat (filename, &statbuf) < 0) - return (ino_t)-1; - - return statbuf.st_ino; -} - -static gboolean -already_warned (const char *name) -{ - static GPtrArray *warnings; - int i; - - if (!warnings) - warnings = g_ptr_array_new (); - - for (i = 0; i < warnings->len; ++i) - { - if (strcmp (warnings->pdata[i], name) == 0) - return TRUE; - } - - g_ptr_array_add (warnings, g_strdup (name)); - - return FALSE; -} - -static const char *const debug_file_directory = DEBUGDIR; - -static ElfParser * -get_build_id_file (ElfParser *elf) -{ - const char *build_id; - GList *tries = NULL, *list; - char *init, *rest; - ElfParser *result = NULL; - char *tmp; - - build_id = elf_parser_get_build_id (elf); - - if (!build_id) - return NULL; - - if (strlen (build_id) < 4) - return NULL; - - init = g_strndup (build_id, 2); - rest = g_strdup_printf ("%s%s", build_id + 2, ".debug"); - - tmp = g_build_filename ( - "/usr", "lib", "debug", ".build-id", init, rest, NULL); - tries = g_list_append (tries, tmp); - - tmp = g_build_filename ( - debug_file_directory, ".build-id", init, rest, NULL); - tries = g_list_append (tries, tmp); - - for (list = tries; list != NULL; list = list->next) - { - char *name = list->data; - ElfParser *parser = elf_parser_new (name, NULL); - - if (parser) - { - const char *file_id = elf_parser_get_build_id (parser); - - if (file_id && strcmp (build_id, file_id) == 0) - { - result = parser; - break; - } - - elf_parser_free (parser); - } - } - - g_list_foreach (tries, (GFunc)g_free, NULL); - g_list_free (tries); - - g_free (init); - g_free (rest); - - return result; -} - -static ElfParser * -get_debuglink_file (ElfParser *elf, - const char *filename, - char **new_name) -{ -#define N_TRIES 4 - const char *basename; - char *dir; - guint32 crc32; - GList *tries = NULL, *list; - ElfParser *result = NULL; - const char *build_id; - - if (!elf) - return NULL; - - basename = elf_parser_get_debug_link (elf, &crc32); - - build_id = elf_parser_get_build_id (elf); - -#if 0 - g_print (" debug link for %s is %s\n", filename, basename); -#endif - - if (!basename) - return NULL; - - dir = g_path_get_dirname (filename); - - tries = g_list_append (tries, g_build_filename (dir, basename, NULL)); - tries = g_list_append (tries, g_build_filename (dir, ".debug", basename, NULL)); - tries = g_list_append (tries, g_build_filename ("/usr", "lib", "debug", dir, basename, NULL)); - tries = g_list_append (tries, g_build_filename (debug_file_directory, dir, basename, NULL)); - - for (list = tries; list != NULL; list = list->next) - { - const char *name = list->data; - ElfParser *parser = elf_parser_new (name, NULL); - guint32 file_crc; - const char *file_build_id; - - if (parser) - { - /* If both files have build ids, and they don't match, - * there is no point computing a CRC32 that we know - * will fail - */ - file_build_id = elf_parser_get_build_id (parser); - if (build_id && file_build_id && strcmp (build_id, file_build_id) != 0) - goto skip; - - file_crc = elf_parser_get_crc32 (parser); - - if (file_crc == crc32) - { - result = parser; - *new_name = g_strdup (name); - break; - } - else - { - if (!already_warned (name)) - { - g_print ("warning: %s has wrong crc %x, %s has crc %x)\n", - name, file_crc, filename, crc32); - } - } - - skip: - elf_parser_free (parser); - } - } - - g_free (dir); - - g_list_foreach (tries, (GFunc)g_free, NULL); - g_list_free (tries); - - return result; -} - -static GList * -get_debug_binaries (GList *files, - ElfParser *elf, - const char *filename) -{ - ElfParser *build_id_file; - GHashTable *seen_names; - GList *free_us = NULL; - - build_id_file = get_build_id_file (elf); - - if (build_id_file) - return g_list_prepend (files, build_id_file); - - /* .gnu_debuglink is actually a chain of debuglinks, and - * there have been real-world cases where following it was - * necessary to get useful debug information. - */ - seen_names = g_hash_table_new (g_str_hash, g_str_equal); - - while (elf) - { - char *debug_name; - - if (g_hash_table_lookup (seen_names, filename)) - break; - - g_hash_table_insert (seen_names, (char *)filename, (char *)filename); - - elf = get_debuglink_file (elf, filename, &debug_name); - - if (elf) - { - files = g_list_prepend (files, elf); - free_us = g_list_prepend (free_us, debug_name); - filename = debug_name; - } - } - - g_list_foreach (free_us, (GFunc)g_free, NULL); - g_list_free (free_us); - - g_hash_table_destroy (seen_names); - - return files; -} - -static char ** -get_lines (const char *format, pid_t pid) -{ - char *filename = g_strdup_printf (format, pid); - char **result = NULL; - char *contents; - - if (g_file_get_contents (filename, &contents, NULL, NULL)) - { - result = g_strsplit (contents, "\n", -1); - - g_free (contents); - } - - g_free (filename); - - return result; -} - -static const uint8_t * -get_vdso_bytes (size_t *length) -{ - static const uint8_t *bytes = NULL; - static size_t n_bytes = 0; - static gboolean has_data; - - if (!has_data) - { - char **lines = get_lines ("/proc/%d/maps", getpid()); - int i; - - for (i = 0; lines[i] != NULL; ++i) - { - char file[256]; - gulong start; - gulong end; - int count = sscanf ( - lines[i], "%lx-%lx %*15s %*x %*x:%*x %*u %255s", - &start, &end, file); - - if (count == 3 && strcmp (file, "[vdso]") == 0) - { - n_bytes = end - start; - - /* Dup the memory here so that valgrind will only - * report one 1 byte invalid read instead of - * a ton when the elf parser scans the vdso - * - * The reason we get a spurious invalid read from - * valgrind is that we are getting the address directly - * from /proc/maps, and valgrind knows that its mmap() - * wrapper never returned that address. But since it - * is a legal mapping, it is legal to read it. - */ - bytes = g_memdup ((uint8_t *)start, n_bytes); - - has_data = TRUE; - } - } - } - - if (length) - *length = n_bytes; - - return bytes; -} - -bin_file_t * -bin_file_new (const char *filename) -{ - ElfParser *elf = NULL; - bin_file_t *bf; - - bf = g_new0 (bin_file_t, 1); - - bf->inode_check = FALSE; - bf->filename = g_strdup (filename); - bf->undefined_name = g_strdup_printf ("In file %s", filename); - bf->ref_count = 1; - bf->elf_files = NULL; - - if (strcmp (filename, "[vdso]") == 0) - { - const guint8 *vdso_bytes; - gsize length; - - vdso_bytes = get_vdso_bytes (&length); - - if (vdso_bytes) - elf = elf_parser_new_from_data (vdso_bytes, length); - } - else - { - elf = elf_parser_new (filename, NULL); - } - - if (elf) - { - /* We need the text offset of the actual binary, not the - * (potential) debug binaries - */ - bf->text_offset = elf_parser_get_text_offset (elf); - - bf->elf_files = get_debug_binaries (bf->elf_files, elf, filename); - bf->elf_files = g_list_append (bf->elf_files, elf); - - bf->inode = read_inode (filename); - } - - return bf; -} - -void -bin_file_free (bin_file_t *bin_file) -{ - if (--bin_file->ref_count == 0) - { - g_list_foreach (bin_file->elf_files, (GFunc)elf_parser_free, NULL); - g_list_free (bin_file->elf_files); - - g_free (bin_file->filename); - g_free (bin_file->undefined_name); - g_free (bin_file); - } -} - -const bin_symbol_t * -bin_file_lookup_symbol (bin_file_t *bin_file, - gulong address) -{ - GList *list; - -#if 0 - g_print ("-=-=-=- \n"); - - g_print ("bin file lookup lookup %d\n", address); -#endif - - address -= bin_file->text_offset; - -#if 0 - g_print ("lookup %d in %s\n", address, bin_file->filename); -#endif - - for (list = bin_file->elf_files; list != NULL; list = list->next) - { - ElfParser *elf = list->data; - const ElfSym *sym = elf_parser_lookup_symbol (elf, address); - - if (sym) - { -#if 0 - g_print ("found %lx => %s\n", address, - bin_symbol_get_name (bin_file, sym)); -#endif - return (const bin_symbol_t *)sym; - } - } - -#if 0 - g_print ("%lx undefined in %s (textoffset %x)\n", - address + bin_file->text_offset, - bin_file->filename, - bin_file->text_offset); -#endif - - return (const bin_symbol_t *)bin_file->undefined_name; -} - -gboolean -bin_file_check_inode (bin_file_t *bin_file, - ino_t inode) -{ - if (bin_file->inode == inode) - return TRUE; - - if (!bin_file->elf_files) - return FALSE; - - if (!bin_file->inode_check) - { - g_print ("warning: Inode mismatch for %s (disk: "FMT64", memory: "FMT64")\n", - bin_file->filename, - (guint64)bin_file->inode, (guint64)inode); - - bin_file->inode_check = TRUE; - } - - return FALSE; -} - -static const ElfSym * -get_elf_sym (bin_file_t *file, - const bin_symbol_t *symbol, - ElfParser **elf_ret) -{ - GList *list; - - for (list = file->elf_files; list != NULL; list = list->next) - { - const ElfSym *sym = (const ElfSym *)symbol; - ElfParser *elf = list->data; - - if (elf_parser_owns_symbol (elf, sym)) - { - *elf_ret = elf; - return sym; - } - } - - g_critical ("Internal error: unrecognized symbol pointer"); - - *elf_ret = NULL; - return NULL; -} - -const char * -bin_symbol_get_name (bin_file_t *file, - const bin_symbol_t *symbol) -{ - if (file->undefined_name == (char *)symbol) - { - return file->undefined_name; - } - else - { - ElfParser *elf; - const ElfSym *sym; - - sym = get_elf_sym (file, symbol, &elf); - - return elf_parser_get_sym_name (elf, sym); - } -} - -gulong -bin_symbol_get_address (bin_file_t *file, - const bin_symbol_t *symbol) -{ - if (file->undefined_name == (char *)symbol) - { - return 0x0; - } - else - { - ElfParser *elf; - const ElfSym *sym; - - sym = get_elf_sym (file, symbol, &elf); - - return elf_parser_get_sym_address (elf, sym); - } -} diff --git a/lib/collector.c b/lib/collector.c deleted file mode 100644 index 8c232fde..00000000 --- a/lib/collector.c +++ /dev/null @@ -1,806 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "stackstash.h" -#include "collector.h" -#include "watch.h" -#include "elfparser.h" -#include "tracker.h" - -#include -#include "util.h" - -#define d_print(...) - -#define N_PAGES 32 /* Number of pages in the ringbuffer */ - -#define N_WAKEUP_EVENTS 149 - -typedef struct counter_t counter_t; -typedef struct sample_event_t sample_event_t; -typedef struct mmap_event_t mmap_event_t; -typedef struct comm_event_t comm_event_t; -typedef struct exit_event_t exit_event_t; -typedef struct fork_event_t fork_event_t; -typedef union counter_event_t counter_event_t; - -static void process_event (Collector *collector, - counter_t *counter, - counter_event_t *event); - -struct counter_t -{ - Collector * collector; - - int fd; - struct perf_event_mmap_page * mmap_page; - uint8_t * data; - - uint64_t tail; - int cpu; -}; - -struct sample_event_t -{ - struct perf_event_header header; - uint64_t ip; - uint32_t pid, tid; - uint64_t n_ips; - uint64_t ips[1]; -}; - -struct comm_event_t -{ - struct perf_event_header header; - uint32_t pid, tid; - char comm[1]; -}; - -struct mmap_event_t -{ - struct perf_event_header header; - - uint32_t pid, tid; - uint64_t addr; - uint64_t len; - uint64_t pgoff; - char filename[1]; -}; - -struct fork_event_t -{ - struct perf_event_header header; - - uint32_t pid, ppid; - uint32_t tid, ptid; -}; - -struct exit_event_t -{ - struct perf_event_header header; - - uint32_t pid, ppid; - uint32_t tid, ptid; -}; - -union counter_event_t -{ - struct perf_event_header header; - mmap_event_t mmap; - comm_event_t comm; - sample_event_t sample; - fork_event_t fork; - exit_event_t exit; -}; - -struct Collector -{ - CollectorFunc callback; - gpointer data; - - tracker_t * tracker; - GTimeVal latest_reset; - - int prev_samples; - int n_samples; - - GList * counters; - - gboolean use_hw_counters; -}; - -static int -get_n_cpus (void) -{ - return sysconf (_SC_NPROCESSORS_ONLN); -} - -static int -sysprof_perf_counter_open (struct perf_event_attr *attr, - pid_t pid, - int cpu, - int group_fd, - unsigned long flags) -{ -#ifndef __NR_perf_counter_open -#if defined(__i386__) -#define __NR_perf_counter_open 336 -#elif defined(__x86_64__) -#define __NR_perf_counter_open 298 -#elif defined(__arm__) -#define __NR_perf_counter_open 364 -#elif defined(__bfin__) -#define __NR_perf_counter_open 369 -#elif defined(__frv__) -#define __NR_perf_counter_open 336 -#elif defined(__m68k__) -#define __NR_perf_counter_open 332 -#elif defined(__MICROBLAZE__) -#define __NR_perf_counter_open 366 -#elif defined(__mips__) && defined(_ABIO32) -#define __NR_perf_counter_open 4333 -#elif defined(__mips__) && defined(_ABIN32) -#define __NR_perf_counter_open 6296 -#elif defined(__mips__) && defined(_ABI64) -#define __NR_perf_counter_open 5292 -#elif defined(__mn10300__) -#define __NR_perf_counter_open 337 -#elif defined(__hppa__) -#define __NR_perf_counter_open 318 -#elif defined(__powerpc__) || defined(__powerpc64__) -#define __NR_perf_counter_open 319 -#elif defined(__s390__) -#define __NR_perf_counter_open 331 -#elif defined(__sh__) && (!defined(__SH5__) || __SH5__ == 32) -#define __NR_perf_counter_open 336 -#elif defined(__sh__) && defined(__SH5__) && __SH5__ == 64 -#define __NR_perf_counter_open 364 -#elif defined(__sparc__) || defined(__sparc64__) -#define __NR_perf_counter_open 327 -#endif -#endif - - attr->size = sizeof(*attr); - - return syscall (__NR_perf_counter_open, attr, pid, cpu, group_fd, flags); -} - - -static double -timeval_to_ms (const GTimeVal *timeval) -{ - return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0; -} - -static double -time_diff (const GTimeVal *first, - const GTimeVal *second) -{ - double first_ms = timeval_to_ms (first); - double second_ms = timeval_to_ms (second); - - return first_ms - second_ms; -} - -#define RESET_DEAD_PERIOD 250 - -static gboolean -in_dead_period (Collector *collector) -{ - GTimeVal now; - double diff; - - g_get_current_time (&now); - - diff = time_diff (&now, &collector->latest_reset); - - if (diff >= 0.0 && diff < RESET_DEAD_PERIOD) - return TRUE; - - return FALSE; -} - -static int -get_page_size (void) -{ - static int page_size; - static gboolean has_page_size = FALSE; - - if (!has_page_size) - { - page_size = getpagesize(); - has_page_size = TRUE; - } - - return page_size; -} - -static void -on_read (gpointer data) -{ - counter_t *counter = data; - int mask = (N_PAGES * get_page_size() - 1); - int n_bytes = mask + 1; - gboolean skip_samples; - Collector *collector; - uint64_t head, tail; - - collector = counter->collector; - - tail = counter->tail; - - head = counter->mmap_page->data_head; - rmb(); - - if (head < tail) - { - g_warning ("sysprof fails at ring buffers (head "FMT64", tail "FMT64"\n", head, tail); - - tail = head; - } - -#if 0 - /* Verify that the double mapping works */ - x = g_random_int() & mask; - g_assert (*(counter->data + x) == *(counter->data + x + n_bytes)); -#endif - - skip_samples = in_dead_period (collector); - -#if 0 - g_print ("n bytes %d\n", head - tail); -#endif - - while (head - tail >= sizeof (struct perf_event_header)) - { - struct perf_event_header *header; - guint8 buffer[4096]; - guint8 *free_me; - - free_me = NULL; - - /* Note that: - * - * - perf events are a multiple of 64 bits - * - the perf event header is 64 bits - * - the data area is a multiple of 64 bits - * - * which means there will always be space for one header, which means we - * can safely dereference the size field. - */ - header = (struct perf_event_header *)(counter->data + (tail & mask)); - - if (header->size > head - tail) - { - /* The kernel did not generate a complete event. - * I don't think that can happen, but we may as well - * be paranoid. - */ - break; - } - - if (counter->data + (tail & mask) + header->size > counter->data + n_bytes) - { - int n_before, n_after; - guint8 *b; - - if (header->size > sizeof (buffer)) - free_me = b = g_malloc (header->size); - else - b = buffer; - - n_after = (tail & mask) + header->size - n_bytes; - n_before = header->size - n_after; - - memcpy (b, counter->data + (tail & mask), n_before); - memcpy (b + n_before, counter->data, n_after); - - header = (struct perf_event_header *)b; - } - - if (!skip_samples || header->type != PERF_RECORD_SAMPLE) - { - if (header->type == PERF_RECORD_SAMPLE) - collector->n_samples++; - - process_event (collector, counter, (counter_event_t *)header); - } - - if (free_me) - g_free (free_me); - - tail += header->size; - } - - counter->tail = tail; - counter->mmap_page->data_tail = tail; - - if (collector->callback) - { - if (collector->n_samples - collector->prev_samples >= N_WAKEUP_EVENTS) - { - gboolean first_sample = collector->prev_samples == 0; - - collector->callback (first_sample, collector->data); - - collector->prev_samples = collector->n_samples; - } - } -} - -static void * -fail (GError **err, const char *what) -{ - g_set_error (err, COLLECTOR_ERROR, COLLECTOR_ERROR_FAILED, - "%s: %s", what, g_strerror (errno)); - - return NULL; -} - -static void * -map_buffer (counter_t *counter, GError **err) -{ - int n_bytes = N_PAGES * get_page_size(); - void *address; - - address = mmap (NULL, n_bytes + get_page_size(), PROT_READ | PROT_WRITE, MAP_SHARED, counter->fd, 0); - - if (address == MAP_FAILED) - return fail (err, "mmap"); - - return address; -} - -static gboolean -counter_set_output (counter_t *counter, int output) -{ - return ioctl (counter->fd, PERF_EVENT_IOC_SET_OUTPUT, output) == 0; -} - -static void -counter_enable (counter_t *counter) -{ - ioctl (counter->fd, PERF_EVENT_IOC_ENABLE); -} - -static void -counter_disable (counter_t *counter) -{ - d_print ("disable\n"); - - ioctl (counter->fd, PERF_EVENT_IOC_DISABLE); -} - -static counter_t * -counter_new (Collector *collector, - pid_t pid, - int cpu, - counter_t *output, - GError **err) -{ - struct perf_event_attr attr; - counter_t *counter; - int fd; - - counter = g_new (counter_t, 1); - - memset (&attr, 0, sizeof (attr)); - - attr.type = PERF_TYPE_HARDWARE; - attr.config = PERF_COUNT_HW_CPU_CYCLES; - attr.sample_period = 1200000 ; /* In number of clock cycles - - * FIXME: consider using frequency instead - */ - attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_CALLCHAIN; - attr.wakeup_events = N_WAKEUP_EVENTS; - attr.disabled = TRUE; - - attr.mmap = 1; - attr.comm = 1; - attr.task = 1; - attr.exclude_idle = 1; - - if (!collector->use_hw_counters || (fd = sysprof_perf_counter_open (&attr, pid, cpu, -1, 0)) < 0) - { - attr.type = PERF_TYPE_SOFTWARE; - attr.config = PERF_COUNT_SW_CPU_CLOCK; - attr.sample_period = 1000000; - - fd = sysprof_perf_counter_open (&attr, pid, cpu, -1, 0); - } - - if (fd < 0) - return fail (err, "Could not open performance counter"); - - counter->collector = collector; - counter->fd = fd; - counter->cpu = cpu; - - if (output && counter_set_output (counter, output->fd)) - { - counter->mmap_page = NULL; - counter->data = NULL; - counter->tail = 0; - } - else - { - counter->mmap_page = map_buffer (counter, err); - - if (!counter->mmap_page || counter->mmap_page == MAP_FAILED) - return NULL; - - counter->data = (uint8_t *)counter->mmap_page + get_page_size (); - counter->tail = 0; - - fd_add_watch (fd, counter); - - fd_set_read_callback (fd, on_read); - } - - return counter; -} - -static void -counter_free (counter_t *counter) -{ - d_print ("munmap\n"); - - munmap (counter->mmap_page, (N_PAGES + 1) * get_page_size()); - fd_remove_watch (counter->fd); - - close (counter->fd); - - g_free (counter); -} - -/* - * Collector - */ -static void -enable_counters (Collector *collector) -{ - GList *list; - - d_print ("enable\n"); - - for (list = collector->counters; list != NULL; list = list->next) - { - counter_t *counter = list->data; - - counter_enable (counter); - } -} - -static void -disable_counters (Collector *collector) -{ - GList *list; - - d_print ("disable\n"); - - for (list = collector->counters; list != NULL; list = list->next) - { - counter_t *counter = list->data; - - counter_disable (counter); - } -} - -void -collector_reset (Collector *collector) -{ - /* Disable the counters so that we won't track - * the activity of tracker_free()/tracker_new() - * - * They will still record fork/mmap/etc. so - * we can keep an accurate log of process creation - */ - if (collector->counters) - { - d_print ("disable counters\n"); - - disable_counters (collector); - } - - if (collector->tracker) - { - tracker_free (collector->tracker); - collector->tracker = tracker_new (); - } - - collector->n_samples = 0; - collector->prev_samples = 0; - - g_get_current_time (&collector->latest_reset); - - if (collector->counters) - { - d_print ("enable counters\n"); - - enable_counters (collector); - } -} - -/* callback is called whenever a new sample arrives */ -Collector * -collector_new (gboolean use_hw_counters, - CollectorFunc callback, - gpointer data) -{ - Collector *collector = g_new0 (Collector, 1); - - collector->callback = callback; - collector->data = data; - collector->tracker = NULL; - collector->use_hw_counters = use_hw_counters; - - collector_reset (collector); - - return collector; -} - -static void -process_mmap (Collector *collector, mmap_event_t *mmap) -{ - tracker_add_map (collector->tracker, - mmap->pid, - mmap->addr, - mmap->addr + mmap->len, - mmap->pgoff, - 0, /* inode */ - mmap->filename); -} - -static void -process_comm (Collector *collector, comm_event_t *comm) -{ - d_print ("pid, tid: %d %d", comm->pid, comm->tid); - - tracker_add_process (collector->tracker, - comm->pid, - comm->comm); -} - -static void -process_fork (Collector *collector, fork_event_t *fork) -{ - d_print ("ppid: %d pid: %d ptid: %d tid %d\n", - fork->ppid, fork->pid, fork->ptid, fork->tid); - - tracker_add_fork (collector->tracker, fork->ppid, fork->pid); -} - -static void -process_exit (Collector *collector, exit_event_t *exit) -{ - d_print ("for %d %d", exit->pid, exit->tid); - - tracker_add_exit (collector->tracker, exit->pid); -} - -static void -process_sample (Collector *collector, - sample_event_t *sample) -{ - uint64_t *ips; - int n_ips; - - d_print ("pid, tid: %d %d", sample->pid, sample->tid); - - if (sample->n_ips == 0) - { - uint64_t trace[3]; - - if (sample->header.misc & PERF_RECORD_MISC_KERNEL) - { - trace[0] = PERF_CONTEXT_KERNEL; - trace[1] = sample->ip; - trace[2] = PERF_CONTEXT_USER; - - ips = trace; - n_ips = 3; - } - else - { - trace[0] = PERF_CONTEXT_USER; - trace[1] = sample->ip; - - ips = trace; - n_ips = 2; - } - } - else - { - ips = sample->ips; - n_ips = sample->n_ips; - } - - tracker_add_sample (collector->tracker, - sample->pid, ips, n_ips); -} - -static void -process_event (Collector *collector, - counter_t *counter, - counter_event_t *event) -{ - char *name; - - switch (event->header.type) - { - case PERF_RECORD_MMAP: name = "mmap"; break; - case PERF_RECORD_LOST: name = "lost"; break; - case PERF_RECORD_COMM: name = "comm"; break; - case PERF_RECORD_EXIT: name = "exit"; break; - case PERF_RECORD_THROTTLE: name = "throttle"; break; - case PERF_RECORD_UNTHROTTLE: name = "unthrottle"; break; - case PERF_RECORD_FORK: name = "fork"; break; - case PERF_RECORD_READ: name = "read"; break; - case PERF_RECORD_SAMPLE: name = "samp"; break; - default: name = "unknown"; break; - } - - d_print ("cpu %d :: %s :: ", counter->cpu, name); - - switch (event->header.type) - { - case PERF_RECORD_MMAP: - process_mmap (collector, &event->mmap); - break; - - case PERF_RECORD_LOST: - g_print ("lost event\n"); - break; - - case PERF_RECORD_COMM: - process_comm (collector, &event->comm); - break; - - case PERF_RECORD_EXIT: - process_exit (collector, &event->exit); - break; - - case PERF_RECORD_THROTTLE: - g_print ("throttle\n"); - break; - - case PERF_RECORD_UNTHROTTLE: - g_print ("unthrottle\n"); - break; - - case PERF_RECORD_FORK: - process_fork (collector, &event->fork); - break; - - case PERF_RECORD_READ: - break; - - case PERF_RECORD_SAMPLE: - process_sample (collector, &event->sample); - break; - - default: - g_warning ("unknown event: %d (%d)\n", - event->header.type, event->header.size); - break; - } - - d_print ("\n"); -} - -gboolean -collector_start (Collector *collector, - pid_t pid, - GError **err) -{ - int n_cpus = get_n_cpus (); - int i; - counter_t *output; - - if (!collector->tracker) - collector->tracker = tracker_new (); - - output = NULL; - for (i = 0; i < n_cpus; ++i) - { - counter_t *counter = counter_new (collector, pid, i, output, err); - - if (!counter) - { - GList *list; - - for (list = collector->counters; list != NULL; list = list->next) - counter_free (list->data); - - collector->tracker = NULL; - - return FALSE; - } - - collector->counters = g_list_append (collector->counters, counter); - - if (!output) - output = counter; - } - - enable_counters (collector); - - return TRUE; -} - -void -collector_stop (Collector *collector) -{ - GList *list; - - if (!collector->counters) - return; - - /* Read any remaining data */ - for (list = collector->counters; list != NULL; list = list->next) - { - counter_t *counter = list->data; - - if (counter->data) - on_read (counter); - - counter_free (counter); - } - - g_list_free (collector->counters); - collector->counters = NULL; -} - -int -collector_get_n_samples (Collector *collector) -{ - return collector->n_samples; -} - -Profile * -collector_create_profile (Collector *collector) -{ - /* The collector must be stopped when you create a profile */ - g_assert (!collector->counters); - - return tracker_create_profile (collector->tracker); -} - -GQuark -collector_error_quark (void) -{ - static GQuark q = 0; - - if (q == 0) - q = g_quark_from_static_string ("collector-error-quark"); - - return q; -} diff --git a/lib/collector.h b/lib/collector.h deleted file mode 100644 index 2689a597..00000000 --- a/lib/collector.h +++ /dev/null @@ -1,46 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "profile.h" - -typedef struct Collector Collector; - -typedef void (* CollectorFunc) (gboolean first_sample, - gpointer data); - -#define COLLECTOR_ERROR collector_error_quark () - -GQuark collector_error_quark (void); - -typedef enum -{ - COLLECTOR_ERROR_FAILED -} CollectorError; - -/* callback is called whenever a new sample arrives */ -Collector *collector_new (gboolean use_hw_counters, - CollectorFunc callback, - gpointer data); -gboolean collector_start (Collector *collector, - pid_t pid, - GError **err); -void collector_stop (Collector *collector); -void collector_reset (Collector *collector); -int collector_get_n_samples (Collector *collector); -Profile * collector_create_profile (Collector *collector); diff --git a/lib/demangle.c b/lib/demangle.c deleted file mode 100644 index ac0381bf..00000000 --- a/lib/demangle.c +++ /dev/null @@ -1,9595 +0,0 @@ -/* - * This file is a concatenation of the files - * - * cp-demangle.c - * cp-dmeangle.h - * cplus-dem.c - * demangle.h - * - * all taken from libiberty in binutils v. 2.16. After this concatenation - * many calls to other functions in libiberty were replaced by calls to - * similar functions in glib. Also global entry points that we don't need - * in sysprof were made static or removed. - * - * Let's hope that no bugs are ever found in this file! - * - * Maybe someday look at what can be deleted from this file - * - * - "mini string library" can be replaced with GString - * - "option" parameter to cplus_demangle can be deleted - * - demangling is always "auto" - */ - -/* Copyright notices: - * - * Demangler for g++ V3 ABI. - * Copyright (C) 2003, 2004 Free Software Foundation, Inc. - * Written by Ian Lance Taylor . - * - * This file is part of the libiberty library, which is part of GCC. - - Defs for interface to demanglers. - Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, - 2003, 2004 Free Software Foundation, Inc. - - Internal demangler interface for g++ V3 ABI. - Copyright (C) 2003, 2004 Free Software Foundation, Inc. - Written by Ian Lance Taylor . - - Demangler for GNU C++ - Copyright 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. - - Written by James Clark (jjc@jclark.uucp) - Rewritten by Fred Fish (fnf@cygnus.com) for ARM and Lucid demangling - Modified by Satish Pai (pai@apollo.hp.com) for HP demangling - - This file 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, 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ - -/* This code implements a demangler for the g++ V3 ABI. The ABI is - described on this web page: - http://www.codesourcery.com/cxx-abi/abi.html#mangling - - This code was written while looking at the demangler written by - Alex Samuel . - - This code first pulls the mangled name apart into a list of - components, and then walks the list generating the demangled - name. - - This file will normally define the following functions, q.v.: - char *cplus_demangle_v3(const char *mangled, int options) - char *java_demangle_v3(const char *mangled) - enum gnu_v3_ctor_kinds is_gnu_v3_mangled_ctor (const char *name) - enum gnu_v3_dtor_kinds is_gnu_v3_mangled_dtor (const char *name) - - Also, the interface to the component list is public, and defined in - demangle.h. The interface consists of these types, which are - defined in demangle.h: - enum demangle_component_type - struct demangle_component - and these functions defined in this file: - cplus_demangle_fill_name - cplus_demangle_fill_extended_operator - cplus_demangle_fill_ctor - cplus_demangle_fill_dtor - cplus_demangle_print - and other functions defined in the file cp-demint.c. - - This file also defines some other functions and variables which are - only to be used by the file cp-demint.c. - - Preprocessor macros you can define while compiling this file: - - IN_LIBGCC2 - If defined, this file defines the following function, q.v.: - char *__cxa_demangle (const char *mangled, char *buf, size_t *len, - int *status) - instead of cplus_demangle_v3() and java_demangle_v3(). - - IN_GLIBCPP_V3 - If defined, this file defines only __cxa_demangle(), and no other - publically visible functions or variables. - - CP_DEMANGLE_DEBUG - If defined, turns on debugging mode, which prints information on - stdout about the mangled string. This is not generally useful. -*/ - -#include -#include -#include -#include -#include -/* Defs for interface to demanglers. - Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, - 2003, 2004 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 2, 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - - -#if !defined (DEMANGLE_H) -#define DEMANGLE_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Options passed to cplus_demangle (in 2nd parameter). */ - -#define DMGL_NO_OPTS 0 /* For readability... */ -#define DMGL_PARAMS (1 << 0) /* Include function args */ -#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ -#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */ -#define DMGL_VERBOSE (1 << 3) /* Include implementation details. */ -#define DMGL_TYPES (1 << 4) /* Also try to demangle type encodings. */ - -#define DMGL_AUTO (1 << 8) -#define DMGL_GNU (1 << 9) -#define DMGL_LUCID (1 << 10) -#define DMGL_ARM (1 << 11) -#define DMGL_HP (1 << 12) /* For the HP aCC compiler; - same as ARM except for - template arguments, etc. */ -#define DMGL_EDG (1 << 13) -#define DMGL_GNU_V3 (1 << 14) -#define DMGL_GNAT (1 << 15) - -/* If none of these are set, use 'current_demangling_style' as the default. */ -#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG|DMGL_GNU_V3|DMGL_JAVA|DMGL_GNAT) - -/* Enumeration of possible demangling styles. - - Lucid and ARM styles are still kept logically distinct, even though - they now both behave identically. The resulting style is actual the - union of both. I.E. either style recognizes both "__pt__" and "__rf__" - for operator "->", even though the first is lucid style and the second - is ARM style. (FIXME?) */ - -enum demangling_styles -{ - no_demangling = -1, - unknown_demangling = 0, - auto_demangling = DMGL_AUTO, - gnu_demangling = DMGL_GNU, - lucid_demangling = DMGL_LUCID, - arm_demangling = DMGL_ARM, - hp_demangling = DMGL_HP, - edg_demangling = DMGL_EDG, - gnu_v3_demangling = DMGL_GNU_V3, - java_demangling = DMGL_JAVA, - gnat_demangling = DMGL_GNAT -}; - -/* Define string names for the various demangling styles. */ - -#define NO_DEMANGLING_STYLE_STRING "none" -#define AUTO_DEMANGLING_STYLE_STRING "auto" -#define GNU_DEMANGLING_STYLE_STRING "gnu" -#define LUCID_DEMANGLING_STYLE_STRING "lucid" -#define ARM_DEMANGLING_STYLE_STRING "arm" -#define HP_DEMANGLING_STYLE_STRING "hp" -#define EDG_DEMANGLING_STYLE_STRING "edg" -#define GNU_V3_DEMANGLING_STYLE_STRING "gnu-v3" -#define JAVA_DEMANGLING_STYLE_STRING "java" -#define GNAT_DEMANGLING_STYLE_STRING "gnat" - -/* Some macros to test what demangling style is active. */ - -#define CURRENT_DEMANGLING_STYLE current_demangling_style -#define AUTO_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_AUTO) -#define GNU_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU) -#define LUCID_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_LUCID) -#define ARM_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_ARM) -#define HP_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_HP) -#define EDG_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_EDG) -#define GNU_V3_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU_V3) -#define JAVA_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_JAVA) -#define GNAT_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNAT) - -/* Provide information about the available demangle styles. This code is - pulled from gdb into libiberty because it is useful to binutils also. */ - -#define PARAMS(a) a - -#define ATTRIBUTE_NORETURN G_GNUC_NORETURN -#define ATTRIBUTE_UNUSED G_GNUC_UNUSED - -static const struct demangler_engine -{ - const char *const demangling_style_name; - const enum demangling_styles demangling_style; - const char *const demangling_style_doc; -} libiberty_demanglers[]; - -#if 0 -extern char * -cplus_demangle PARAMS ((const char *mangled, int options)); -#endif - -#if 0 -extern int -cplus_demangle_opname PARAMS ((const char *opname, char *result, int options)); - -extern const char * -cplus_mangle_opname PARAMS ((const char *opname, int options)); -#endif - -/* Note: This sets global state. FIXME if you care about multi-threading. */ - -enum gnu_v3_ctor_kinds { - gnu_v3_complete_object_ctor = 1, - gnu_v3_base_object_ctor, - gnu_v3_complete_object_allocating_ctor -}; - -/* Return non-zero iff NAME is the mangled form of a constructor name - in the G++ V3 ABI demangling style. Specifically, return an `enum - gnu_v3_ctor_kinds' value indicating what kind of constructor - it is. */ -extern enum gnu_v3_ctor_kinds - is_gnu_v3_mangled_ctor PARAMS ((const char *name)); - - -enum gnu_v3_dtor_kinds { - gnu_v3_deleting_dtor = 1, - gnu_v3_complete_object_dtor, - gnu_v3_base_object_dtor -}; - -/* Return non-zero iff NAME is the mangled form of a destructor name - in the G++ V3 ABI demangling style. Specifically, return an `enum - gnu_v3_dtor_kinds' value, indicating what kind of destructor - it is. */ -extern enum gnu_v3_dtor_kinds - is_gnu_v3_mangled_dtor PARAMS ((const char *name)); - -/* The V3 demangler works in two passes. The first pass builds a tree - representation of the mangled name, and the second pass turns the - tree representation into a demangled string. Here we define an - interface to permit a caller to build their own tree - representation, which they can pass to the demangler to get a - demangled string. This can be used to canonicalize user input into - something which the demangler might output. It could also be used - by other demanglers in the future. */ - -/* These are the component types which may be found in the tree. Many - component types have one or two subtrees, referred to as left and - right (a component type with only one subtree puts it in the left - subtree). */ - -enum demangle_component_type -{ - /* A name, with a length and a pointer to a string. */ - DEMANGLE_COMPONENT_NAME, - /* A qualified name. The left subtree is a class or namespace or - some such thing, and the right subtree is a name qualified by - that class. */ - DEMANGLE_COMPONENT_QUAL_NAME, - /* A local name. The left subtree describes a function, and the - right subtree is a name which is local to that function. */ - DEMANGLE_COMPONENT_LOCAL_NAME, - /* A typed name. The left subtree is a name, and the right subtree - describes that name as a function. */ - DEMANGLE_COMPONENT_TYPED_NAME, - /* A template. The left subtree is a template name, and the right - subtree is a template argument list. */ - DEMANGLE_COMPONENT_TEMPLATE, - /* A template parameter. This holds a number, which is the template - parameter index. */ - DEMANGLE_COMPONENT_TEMPLATE_PARAM, - /* A constructor. This holds a name and the kind of - constructor. */ - DEMANGLE_COMPONENT_CTOR, - /* A destructor. This holds a name and the kind of destructor. */ - DEMANGLE_COMPONENT_DTOR, - /* A vtable. This has one subtree, the type for which this is a - vtable. */ - DEMANGLE_COMPONENT_VTABLE, - /* A VTT structure. This has one subtree, the type for which this - is a VTT. */ - DEMANGLE_COMPONENT_VTT, - /* A construction vtable. The left subtree is the type for which - this is a vtable, and the right subtree is the derived type for - which this vtable is built. */ - DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE, - /* A typeinfo structure. This has one subtree, the type for which - this is the tpeinfo structure. */ - DEMANGLE_COMPONENT_TYPEINFO, - /* A typeinfo name. This has one subtree, the type for which this - is the typeinfo name. */ - DEMANGLE_COMPONENT_TYPEINFO_NAME, - /* A typeinfo function. This has one subtree, the type for which - this is the tpyeinfo function. */ - DEMANGLE_COMPONENT_TYPEINFO_FN, - /* A thunk. This has one subtree, the name for which this is a - thunk. */ - DEMANGLE_COMPONENT_THUNK, - /* A virtual thunk. This has one subtree, the name for which this - is a virtual thunk. */ - DEMANGLE_COMPONENT_VIRTUAL_THUNK, - /* A covariant thunk. This has one subtree, the name for which this - is a covariant thunk. */ - DEMANGLE_COMPONENT_COVARIANT_THUNK, - /* A Java class. This has one subtree, the type. */ - DEMANGLE_COMPONENT_JAVA_CLASS, - /* A guard variable. This has one subtree, the name for which this - is a guard variable. */ - DEMANGLE_COMPONENT_GUARD, - /* A reference temporary. This has one subtree, the name for which - this is a temporary. */ - DEMANGLE_COMPONENT_REFTEMP, - /* A standard substitution. This holds the name of the - substitution. */ - DEMANGLE_COMPONENT_SUB_STD, - /* The restrict qualifier. The one subtree is the type which is - being qualified. */ - DEMANGLE_COMPONENT_RESTRICT, - /* The volatile qualifier. The one subtree is the type which is - being qualified. */ - DEMANGLE_COMPONENT_VOLATILE, - /* The const qualifier. The one subtree is the type which is being - qualified. */ - DEMANGLE_COMPONENT_CONST, - /* The restrict qualifier modifying a member function. The one - subtree is the type which is being qualified. */ - DEMANGLE_COMPONENT_RESTRICT_THIS, - /* The volatile qualifier modifying a member function. The one - subtree is the type which is being qualified. */ - DEMANGLE_COMPONENT_VOLATILE_THIS, - /* The const qualifier modifying a member function. The one subtree - is the type which is being qualified. */ - DEMANGLE_COMPONENT_CONST_THIS, - /* A vendor qualifier. The left subtree is the type which is being - qualified, and the right subtree is the name of the - qualifier. */ - DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL, - /* A pointer. The one subtree is the type which is being pointed - to. */ - DEMANGLE_COMPONENT_POINTER, - /* A reference. The one subtree is the type which is being - referenced. */ - DEMANGLE_COMPONENT_REFERENCE, - /* A complex type. The one subtree is the base type. */ - DEMANGLE_COMPONENT_COMPLEX, - /* An imaginary type. The one subtree is the base type. */ - DEMANGLE_COMPONENT_IMAGINARY, - /* A builtin type. This holds the builtin type information. */ - DEMANGLE_COMPONENT_BUILTIN_TYPE, - /* A vendor's builtin type. This holds the name of the type. */ - DEMANGLE_COMPONENT_VENDOR_TYPE, - /* A function type. The left subtree is the return type. The right - subtree is a list of ARGLIST nodes. Either or both may be - NULL. */ - DEMANGLE_COMPONENT_FUNCTION_TYPE, - /* An array type. The left subtree is the dimension, which may be - NULL, or a string (represented as DEMANGLE_COMPONENT_NAME), or an - expression. The right subtree is the element type. */ - DEMANGLE_COMPONENT_ARRAY_TYPE, - /* A pointer to member type. The left subtree is the class type, - and the right subtree is the member type. CV-qualifiers appear - on the latter. */ - DEMANGLE_COMPONENT_PTRMEM_TYPE, - /* An argument list. The left subtree is the current argument, and - the right subtree is either NULL or another ARGLIST node. */ - DEMANGLE_COMPONENT_ARGLIST, - /* A template argument list. The left subtree is the current - template argument, and the right subtree is either NULL or - another TEMPLATE_ARGLIST node. */ - DEMANGLE_COMPONENT_TEMPLATE_ARGLIST, - /* An operator. This holds information about a standard - operator. */ - DEMANGLE_COMPONENT_OPERATOR, - /* An extended operator. This holds the number of arguments, and - the name of the extended operator. */ - DEMANGLE_COMPONENT_EXTENDED_OPERATOR, - /* A typecast, represented as a unary operator. The one subtree is - the type to which the argument should be cast. */ - DEMANGLE_COMPONENT_CAST, - /* A unary expression. The left subtree is the operator, and the - right subtree is the single argument. */ - DEMANGLE_COMPONENT_UNARY, - /* A binary expression. The left subtree is the operator, and the - right subtree is a BINARY_ARGS. */ - DEMANGLE_COMPONENT_BINARY, - /* Arguments to a binary expression. The left subtree is the first - argument, and the right subtree is the second argument. */ - DEMANGLE_COMPONENT_BINARY_ARGS, - /* A trinary expression. The left subtree is the operator, and the - right subtree is a TRINARY_ARG1. */ - DEMANGLE_COMPONENT_TRINARY, - /* Arguments to a trinary expression. The left subtree is the first - argument, and the right subtree is a TRINARY_ARG2. */ - DEMANGLE_COMPONENT_TRINARY_ARG1, - /* More arguments to a trinary expression. The left subtree is the - second argument, and the right subtree is the third argument. */ - DEMANGLE_COMPONENT_TRINARY_ARG2, - /* A literal. The left subtree is the type, and the right subtree - is the value, represented as a DEMANGLE_COMPONENT_NAME. */ - DEMANGLE_COMPONENT_LITERAL, - /* A negative literal. Like LITERAL, but the value is negated. - This is a minor hack: the NAME used for LITERAL points directly - to the mangled string, but since negative numbers are mangled - using 'n' instead of '-', we want a way to indicate a negative - number which involves neither modifying the mangled string nor - allocating a new copy of the literal in memory. */ - DEMANGLE_COMPONENT_LITERAL_NEG -}; - -/* Types which are only used internally. */ - -struct demangle_operator_info; -struct demangle_builtin_type_info; - -/* A node in the tree representation is an instance of a struct - demangle_component. Note that the field names of the struct are - not well protected against macros defined by the file including - this one. We can fix this if it ever becomes a problem. */ - -struct demangle_component -{ - /* The type of this component. */ - enum demangle_component_type type; - - union - { - /* For DEMANGLE_COMPONENT_NAME. */ - struct - { - /* A pointer to the name (which need not NULL terminated) and - its length. */ - const char *s; - int len; - } s_name; - - /* For DEMANGLE_COMPONENT_OPERATOR. */ - struct - { - /* Operator. */ - const struct demangle_operator_info *op; - } s_operator; - - /* For DEMANGLE_COMPONENT_EXTENDED_OPERATOR. */ - struct - { - /* Number of arguments. */ - int args; - /* Name. */ - struct demangle_component *name; - } s_extended_operator; - - /* For DEMANGLE_COMPONENT_CTOR. */ - struct - { - /* Kind of constructor. */ - enum gnu_v3_ctor_kinds kind; - /* Name. */ - struct demangle_component *name; - } s_ctor; - - /* For DEMANGLE_COMPONENT_DTOR. */ - struct - { - /* Kind of destructor. */ - enum gnu_v3_dtor_kinds kind; - /* Name. */ - struct demangle_component *name; - } s_dtor; - - /* For DEMANGLE_COMPONENT_BUILTIN_TYPE. */ - struct - { - /* Builtin type. */ - const struct demangle_builtin_type_info *type; - } s_builtin; - - /* For DEMANGLE_COMPONENT_SUB_STD. */ - struct - { - /* Standard substitution string. */ - const char* string; - /* Length of string. */ - int len; - } s_string; - - /* For DEMANGLE_COMPONENT_TEMPLATE_PARAM. */ - struct - { - /* Template parameter index. */ - long number; - } s_number; - - /* For other types. */ - struct - { - /* Left (or only) subtree. */ - struct demangle_component *left; - /* Right subtree. */ - struct demangle_component *right; - } s_binary; - - } u; -}; - -/* People building mangled trees are expected to allocate instances of - struct demangle_component themselves. They can then call one of - the following functions to fill them in. */ - -/* Fill in most component types with a left subtree and a right - subtree. Returns non-zero on success, zero on failure, such as an - unrecognized or inappropriate component type. */ - -extern int -cplus_demangle_fill_component PARAMS ((struct demangle_component *fill, - enum demangle_component_type, - struct demangle_component *left, - struct demangle_component *right)); - -/* Fill in a DEMANGLE_COMPONENT_NAME. Returns non-zero on success, - zero for bad arguments. */ - -extern int -cplus_demangle_fill_name PARAMS ((struct demangle_component *fill, - const char *, int)); - -/* Fill in a DEMANGLE_COMPONENT_BUILTIN_TYPE, using the name of the - builtin type (e.g., "int", etc.). Returns non-zero on success, - zero if the type is not recognized. */ - -extern int -cplus_demangle_fill_builtin_type PARAMS ((struct demangle_component *fill, - const char *type_name)); - -/* Fill in a DEMANGLE_COMPONENT_OPERATOR, using the name of the - operator and the number of arguments which it takes (the latter is - used to disambiguate operators which can be both binary and unary, - such as '-'). Returns non-zero on success, zero if the operator is - not recognized. */ - -extern int -cplus_demangle_fill_operator PARAMS ((struct demangle_component *fill, - const char *opname, int args)); - -/* Fill in a DEMANGLE_COMPONENT_EXTENDED_OPERATOR, providing the - number of arguments and the name. Returns non-zero on success, - zero for bad arguments. */ - -extern int -cplus_demangle_fill_extended_operator PARAMS ((struct demangle_component *fill, - int numargs, - struct demangle_component *nm)); - -/* Fill in a DEMANGLE_COMPONENT_CTOR. Returns non-zero on success, - zero for bad arguments. */ - -extern int -cplus_demangle_fill_ctor PARAMS ((struct demangle_component *fill, - enum gnu_v3_ctor_kinds kind, - struct demangle_component *name)); - -/* Fill in a DEMANGLE_COMPONENT_DTOR. Returns non-zero on success, - zero for bad arguments. */ - -extern int -cplus_demangle_fill_dtor PARAMS ((struct demangle_component *fill, - enum gnu_v3_dtor_kinds kind, - struct demangle_component *name)); - -/* This function translates a mangled name into a struct - demangle_component tree. The first argument is the mangled name. - The second argument is DMGL_* options. This returns a pointer to a - tree on success, or NULL on failure. On success, the third - argument is set to a block of memory allocated by malloc. This - block should be passed to free when the tree is no longer - needed. */ - -extern struct demangle_component * -cplus_demangle_v3_components PARAMS ((const char *mangled, - int options, - void **mem)); - -/* This function takes a struct demangle_component tree and returns - the corresponding demangled string. The first argument is DMGL_* - options. The second is the tree to demangle. The third is a guess - at the length of the demangled string, used to initially allocate - the return buffer. The fourth is a pointer to a size_t. On - success, this function returns a buffer allocated by malloc(), and - sets the size_t pointed to by the fourth argument to the size of - the allocated buffer (not the length of the returned string). On - failure, this function returns NULL, and sets the size_t pointed to - by the fourth argument to 0 for an invalid tree, or to 1 for a - memory allocation error. */ - -extern char * -cplus_demangle_print PARAMS ((int options, - const struct demangle_component *tree, - int estimated_length, - size_t *p_allocated_size)); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* DEMANGLE_H */ - - -/* V3 ABI demangling entry points, defined in cp-demangle.c. */ -static char* cplus_demangle_v3 PARAMS ((const char* mangled, int options)); - -static char* java_demangle_v3 PARAMS ((const char* mangled)); - -/* Internal demangler interface for g++ V3 ABI. - Copyright (C) 2003, 2004 Free Software Foundation, Inc. - Written by Ian Lance Taylor . - - This file is part of the libiberty library, which is part of GCC. - - This file 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. - - In addition to the permissions in the GNU General Public License, the - Free Software Foundation gives you unlimited permission to link the - compiled version of this file into combinations with other programs, - and to distribute those combinations without any restriction coming - from the use of this file. (The General Public License restrictions - do apply in other respects; for example, they cover modification of - the file, and distribution when not linked into a combined - executable.) - - 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -/* This file provides some definitions shared by cp-demangle.c and - cp-demint.c. It should not be included by any other files. */ - -/* Information we keep for operators. */ - -struct demangle_operator_info -{ - /* Mangled name. */ - const char *code; - /* Real name. */ - const char *name; - /* Length of real name. */ - int len; - /* Number of arguments. */ - int args; -}; - -/* How to print the value of a builtin type. */ - -enum d_builtin_type_print -{ - /* Print as (type)val. */ - D_PRINT_DEFAULT, - /* Print as integer. */ - D_PRINT_INT, - /* Print as unsigned integer, with trailing "u". */ - D_PRINT_UNSIGNED, - /* Print as long, with trailing "l". */ - D_PRINT_LONG, - /* Print as unsigned long, with trailing "ul". */ - D_PRINT_UNSIGNED_LONG, - /* Print as long long, with trailing "ll". */ - D_PRINT_LONG_LONG, - /* Print as unsigned long long, with trailing "ull". */ - D_PRINT_UNSIGNED_LONG_LONG, - /* Print as bool. */ - D_PRINT_BOOL, - /* Print as float--put value in square brackets. */ - D_PRINT_FLOAT, - /* Print in usual way, but here to detect void. */ - D_PRINT_VOID -}; - -/* Information we keep for a builtin type. */ - -struct demangle_builtin_type_info -{ - /* Type name. */ - const char *name; - /* Length of type name. */ - int len; - /* Type name when using Java. */ - const char *java_name; - /* Length of java name. */ - int java_len; - /* How to print a value of this type. */ - enum d_builtin_type_print print; -}; - -/* The information structure we pass around. */ - -struct d_info -{ - /* The string we are demangling. */ - const char *s; - /* The end of the string we are demangling. */ - const char *send; - /* The options passed to the demangler. */ - int options; - /* The next character in the string to consider. */ - const char *n; - /* The array of components. */ - struct demangle_component *comps; - /* The index of the next available component. */ - int next_comp; - /* The number of available component structures. */ - int num_comps; - /* The array of substitutions. */ - struct demangle_component **subs; - /* The index of the next substitution. */ - int next_sub; - /* The number of available entries in the subs array. */ - int num_subs; - /* The number of substitutions which we actually made from the subs - array, plus the number of template parameter references we - saw. */ - int did_subs; - /* The last name we saw, for constructors and destructors. */ - struct demangle_component *last_name; - /* A running total of the length of large expansions from the - mangled name to the demangled name, such as standard - substitutions and builtin types. */ - int expansion; -}; - -#define d_peek_char(di) (*((di)->n)) -#define d_peek_next_char(di) ((di)->n[1]) -#define d_advance(di, i) ((di)->n += (i)) -#define d_next_char(di) (*((di)->n++)) -#define d_str(di) ((di)->n) - -/* Functions and arrays in cp-demangle.c which are referenced by - functions in cp-demint.c. */ -#define CP_STATIC_IF_GLIBCPP_V3 static - -#define D_BUILTIN_TYPE_COUNT (26) - -#if 0 -static struct demangle_component * -cplus_demangle_mangled_name PARAMS ((struct d_info *, int)); -#endif - -#if 0 -static struct demangle_component * -cplus_demangle_type PARAMS ((struct d_info *)); -#endif - -extern void -cplus_demangle_init_info PARAMS ((const char *, int, size_t, struct d_info *)); - -#if 0 -/* cp-demangle.c needs to define this a little differently */ -#undef CP_STATIC_IF_GLIBCPP_V3 -#endif -#define IN_GLIBCPP_V3 - -/* If IN_GLIBCPP_V3 is defined, some functions are made static. We - also rename them via #define to avoid compiler errors when the - static definition conflicts with the extern declaration in a header - file. */ -#ifdef IN_GLIBCPP_V3 - -#define CP_STATIC_IF_GLIBCPP_V3 static - -#define cplus_demangle_fill_name d_fill_name -static int -d_fill_name PARAMS ((struct demangle_component *, const char *, int)); - -#define cplus_demangle_fill_extended_operator d_fill_extended_operator -static int -d_fill_extended_operator PARAMS ((struct demangle_component *, int, - struct demangle_component *)); - -#define cplus_demangle_fill_ctor d_fill_ctor -static int -d_fill_ctor PARAMS ((struct demangle_component *, enum gnu_v3_ctor_kinds, - struct demangle_component *)); - -#define cplus_demangle_fill_dtor d_fill_dtor -static int -d_fill_dtor PARAMS ((struct demangle_component *, enum gnu_v3_dtor_kinds, - struct demangle_component *)); - -#define cplus_demangle_mangled_name d_mangled_name -static struct demangle_component * -d_mangled_name PARAMS ((struct d_info *, int)); - -#define cplus_demangle_type d_type -static struct demangle_component * -d_type PARAMS ((struct d_info *)); - -#define cplus_demangle_print d_print -static char * -d_print PARAMS ((int, const struct demangle_component *, int, size_t *)); - -#define cplus_demangle_init_info d_init_info -static void -d_init_info PARAMS ((const char *, int, size_t, struct d_info *)); - -#else /* ! defined(IN_GLIBCPP_V3) */ -#define CP_STATIC_IF_GLIBCPP_V3 -#endif /* ! defined(IN_GLIBCPP_V3) */ - -/* See if the compiler supports dynamic arrays. */ - -#ifdef __GNUC__ -#define CP_DYNAMIC_ARRAYS -#else -#ifdef __STDC__ -#ifdef __STDC_VERSION__ -#if __STDC_VERSION__ >= 199901L -#define CP_DYNAMIC_ARRAYS -#endif /* __STDC__VERSION >= 199901L */ -#endif /* defined (__STDC_VERSION__) */ -#endif /* defined (__STDC__) */ -#endif /* ! defined (__GNUC__) */ - -/* We avoid pulling in the ctype tables, to prevent pulling in - additional unresolved symbols when this code is used in a library. - FIXME: Is this really a valid reason? This comes from the original - V3 demangler code. - - As of this writing this file has the following undefined references - when compiled with -DIN_GLIBCPP_V3: malloc, realloc, free, memcpy, - strcpy, strcat, strlen. */ - -#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') -#define IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z') -#define IS_LOWER(c) ((c) >= 'a' && (c) <= 'z') - -/* The prefix prepended by GCC to an identifier represnting the - anonymous namespace. */ -#define ANONYMOUS_NAMESPACE_PREFIX "_GLOBAL_" -#define ANONYMOUS_NAMESPACE_PREFIX_LEN \ - (sizeof (ANONYMOUS_NAMESPACE_PREFIX) - 1) - -/* Information we keep for the standard substitutions. */ - -struct d_standard_sub_info -{ - /* The code for this substitution. */ - char code; - /* The simple string it expands to. */ - const char *simple_expansion; - /* The length of the simple expansion. */ - int simple_len; - /* The results of a full, verbose, expansion. This is used when - qualifying a constructor/destructor, or when in verbose mode. */ - const char *full_expansion; - /* The length of the full expansion. */ - int full_len; - /* What to set the last_name field of d_info to; NULL if we should - not set it. This is only relevant when qualifying a - constructor/destructor. */ - const char *set_last_name; - /* The length of set_last_name. */ - int set_last_name_len; -}; - -/* Accessors for subtrees of struct demangle_component. */ - -#define d_left(dc) ((dc)->u.s_binary.left) -#define d_right(dc) ((dc)->u.s_binary.right) - -/* A list of templates. This is used while printing. */ - -struct d_print_template -{ - /* Next template on the list. */ - struct d_print_template *next; - /* This template. */ - const struct demangle_component *template; -}; - -/* A list of type modifiers. This is used while printing. */ - -struct d_print_mod -{ - /* Next modifier on the list. These are in the reverse of the order - in which they appeared in the mangled string. */ - struct d_print_mod *next; - /* The modifier. */ - const struct demangle_component *mod; - /* Whether this modifier was printed. */ - int printed; - /* The list of templates which applies to this modifier. */ - struct d_print_template *templates; -}; - -/* We use this structure to hold information during printing. */ - -struct d_print_info -{ - /* The options passed to the demangler. */ - int options; - /* Buffer holding the result. */ - char *buf; - /* Current length of data in buffer. */ - size_t len; - /* Allocated size of buffer. */ - size_t alc; - /* The current list of templates, if any. */ - struct d_print_template *templates; - /* The current list of modifiers (e.g., pointer, reference, etc.), - if any. */ - struct d_print_mod *modifiers; - /* Set to 1 if we had a memory allocation failure. */ - int allocation_failure; -}; - -#define d_print_saw_error(dpi) ((dpi)->buf == NULL) - -#define d_append_char(dpi, c) \ - do \ - { \ - if ((dpi)->buf != NULL && (dpi)->len < (dpi)->alc) \ - (dpi)->buf[(dpi)->len++] = (c); \ - else \ - d_print_append_char ((dpi), (c)); \ - } \ - while (0) - -#define d_append_buffer(dpi, s, l) \ - do \ - { \ - if ((dpi)->buf != NULL && (dpi)->len + (l) <= (dpi)->alc) \ - { \ - memcpy ((dpi)->buf + (dpi)->len, (s), (l)); \ - (dpi)->len += l; \ - } \ - else \ - d_print_append_buffer ((dpi), (s), (l)); \ - } \ - while (0) - -#define d_append_string_constant(dpi, s) \ - d_append_buffer (dpi, (s), sizeof (s) - 1) - -#define d_last_char(dpi) \ - ((dpi)->buf == NULL || (dpi)->len == 0 ? '\0' : (dpi)->buf[(dpi)->len - 1]) - -#ifdef CP_DEMANGLE_DEBUG -static void -d_dump PARAMS ((struct demangle_component *, int)); -#endif - -static struct demangle_component * -d_make_empty PARAMS ((struct d_info *)); - -static struct demangle_component * -d_make_comp PARAMS ((struct d_info *, enum demangle_component_type, - struct demangle_component *, - struct demangle_component *)); - -static struct demangle_component * -d_make_name PARAMS ((struct d_info *, const char *, int)); - -static struct demangle_component * -d_make_builtin_type PARAMS ((struct d_info *, - const struct demangle_builtin_type_info *)); - -static struct demangle_component * -d_make_operator PARAMS ((struct d_info *, - const struct demangle_operator_info *)); - -static struct demangle_component * -d_make_extended_operator PARAMS ((struct d_info *, int, - struct demangle_component *)); - -static struct demangle_component * -d_make_ctor PARAMS ((struct d_info *, enum gnu_v3_ctor_kinds, - struct demangle_component *)); - -static struct demangle_component * -d_make_dtor PARAMS ((struct d_info *, enum gnu_v3_dtor_kinds, - struct demangle_component *)); - -static struct demangle_component * -d_make_template_param PARAMS ((struct d_info *, long)); - -static struct demangle_component * -d_make_sub PARAMS ((struct d_info *, const char *, int)); - -static int -has_return_type PARAMS ((struct demangle_component *)); - -static int -is_ctor_dtor_or_conversion PARAMS ((struct demangle_component *)); - -static struct demangle_component * -d_encoding PARAMS ((struct d_info *, int)); - -static struct demangle_component * -d_name PARAMS ((struct d_info *)); - -static struct demangle_component * -d_nested_name PARAMS ((struct d_info *)); - -static struct demangle_component * -d_prefix PARAMS ((struct d_info *)); - -static struct demangle_component * -d_unqualified_name PARAMS ((struct d_info *)); - -static struct demangle_component * -d_source_name PARAMS ((struct d_info *)); - -static long -d_number PARAMS ((struct d_info *)); - -static struct demangle_component * -d_identifier PARAMS ((struct d_info *, int)); - -static struct demangle_component * -d_operator_name PARAMS ((struct d_info *)); - -static struct demangle_component * -d_special_name PARAMS ((struct d_info *)); - -static int -d_call_offset PARAMS ((struct d_info *, int)); - -static struct demangle_component * -d_ctor_dtor_name PARAMS ((struct d_info *)); - -static struct demangle_component ** -d_cv_qualifiers PARAMS ((struct d_info *, struct demangle_component **, int)); - -static struct demangle_component * -d_function_type PARAMS ((struct d_info *)); - -static struct demangle_component * -d_bare_function_type PARAMS ((struct d_info *, int)); - -static struct demangle_component * -d_class_enum_type PARAMS ((struct d_info *)); - -static struct demangle_component * -d_array_type PARAMS ((struct d_info *)); - -static struct demangle_component * -d_pointer_to_member_type PARAMS ((struct d_info *)); - -static struct demangle_component * -d_template_param PARAMS ((struct d_info *)); - -static struct demangle_component * -d_template_args PARAMS ((struct d_info *)); - -static struct demangle_component * -d_template_arg PARAMS ((struct d_info *)); - -static struct demangle_component * -d_expression PARAMS ((struct d_info *)); - -static struct demangle_component * -d_expr_primary PARAMS ((struct d_info *)); - -static struct demangle_component * -d_local_name PARAMS ((struct d_info *)); - -static int -d_discriminator PARAMS ((struct d_info *)); - -static int -d_add_substitution PARAMS ((struct d_info *, struct demangle_component *)); - -static struct demangle_component * -d_substitution PARAMS ((struct d_info *, int)); - -static void -d_print_resize PARAMS ((struct d_print_info *, size_t)); - -static void -d_print_append_char PARAMS ((struct d_print_info *, int)); - -static void -d_print_append_buffer PARAMS ((struct d_print_info *, const char *, size_t)); - -static void -d_print_error PARAMS ((struct d_print_info *)); - -static void -d_print_comp PARAMS ((struct d_print_info *, - const struct demangle_component *)); - -static void -d_print_java_identifier PARAMS ((struct d_print_info *, const char *, int)); - -static void -d_print_mod_list PARAMS ((struct d_print_info *, struct d_print_mod *, int)); - -static void -d_print_mod PARAMS ((struct d_print_info *, - const struct demangle_component *)); - -static void -d_print_function_type PARAMS ((struct d_print_info *, - const struct demangle_component *, - struct d_print_mod *)); - -static void -d_print_array_type PARAMS ((struct d_print_info *, - const struct demangle_component *, - struct d_print_mod *)); - -static void -d_print_expr_op PARAMS ((struct d_print_info *, - const struct demangle_component *)); - -static void -d_print_cast PARAMS ((struct d_print_info *, - const struct demangle_component *)); - -static char * -d_demangle PARAMS ((const char *, int, size_t *)); - -#ifdef CP_DEMANGLE_DEBUG - -static void -d_dump (dc, indent) - struct demangle_component *dc; - int indent; -{ - int i; - - if (dc == NULL) - return; - - for (i = 0; i < indent; ++i) - putchar (' '); - - switch (dc->type) - { - case DEMANGLE_COMPONENT_NAME: - printf ("name '%.*s'\n", dc->u.s_name.len, dc->u.s_name.s); - return; - case DEMANGLE_COMPONENT_TEMPLATE_PARAM: - printf ("template parameter %ld\n", dc->u.s_number.number); - return; - case DEMANGLE_COMPONENT_CTOR: - printf ("constructor %d\n", (int) dc->u.s_ctor.kind); - d_dump (dc->u.s_ctor.name, indent + 2); - return; - case DEMANGLE_COMPONENT_DTOR: - printf ("destructor %d\n", (int) dc->u.s_dtor.kind); - d_dump (dc->u.s_dtor.name, indent + 2); - return; - case DEMANGLE_COMPONENT_SUB_STD: - printf ("standard substitution %s\n", dc->u.s_string.string); - return; - case DEMANGLE_COMPONENT_BUILTIN_TYPE: - printf ("builtin type %s\n", dc->u.s_builtin.type->name); - return; - case DEMANGLE_COMPONENT_OPERATOR: - printf ("operator %s\n", dc->u.s_operator.op->name); - return; - case DEMANGLE_COMPONENT_EXTENDED_OPERATOR: - printf ("extended operator with %d args\n", - dc->u.s_extended_operator.args); - d_dump (dc->u.s_extended_operator.name, indent + 2); - return; - - case DEMANGLE_COMPONENT_QUAL_NAME: - printf ("qualified name\n"); - break; - case DEMANGLE_COMPONENT_LOCAL_NAME: - printf ("local name\n"); - break; - case DEMANGLE_COMPONENT_TYPED_NAME: - printf ("typed name\n"); - break; - case DEMANGLE_COMPONENT_TEMPLATE: - printf ("template\n"); - break; - case DEMANGLE_COMPONENT_VTABLE: - printf ("vtable\n"); - break; - case DEMANGLE_COMPONENT_VTT: - printf ("VTT\n"); - break; - case DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE: - printf ("construction vtable\n"); - break; - case DEMANGLE_COMPONENT_TYPEINFO: - printf ("typeinfo\n"); - break; - case DEMANGLE_COMPONENT_TYPEINFO_NAME: - printf ("typeinfo name\n"); - break; - case DEMANGLE_COMPONENT_TYPEINFO_FN: - printf ("typeinfo function\n"); - break; - case DEMANGLE_COMPONENT_THUNK: - printf ("thunk\n"); - break; - case DEMANGLE_COMPONENT_VIRTUAL_THUNK: - printf ("virtual thunk\n"); - break; - case DEMANGLE_COMPONENT_COVARIANT_THUNK: - printf ("covariant thunk\n"); - break; - case DEMANGLE_COMPONENT_JAVA_CLASS: - printf ("java class\n"); - break; - case DEMANGLE_COMPONENT_GUARD: - printf ("guard\n"); - break; - case DEMANGLE_COMPONENT_REFTEMP: - printf ("reference temporary\n"); - break; - case DEMANGLE_COMPONENT_RESTRICT: - printf ("restrict\n"); - break; - case DEMANGLE_COMPONENT_VOLATILE: - printf ("volatile\n"); - break; - case DEMANGLE_COMPONENT_CONST: - printf ("const\n"); - break; - case DEMANGLE_COMPONENT_RESTRICT_THIS: - printf ("restrict this\n"); - break; - case DEMANGLE_COMPONENT_VOLATILE_THIS: - printf ("volatile this\n"); - break; - case DEMANGLE_COMPONENT_CONST_THIS: - printf ("const this\n"); - break; - case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: - printf ("vendor type qualifier\n"); - break; - case DEMANGLE_COMPONENT_POINTER: - printf ("pointer\n"); - break; - case DEMANGLE_COMPONENT_REFERENCE: - printf ("reference\n"); - break; - case DEMANGLE_COMPONENT_COMPLEX: - printf ("complex\n"); - break; - case DEMANGLE_COMPONENT_IMAGINARY: - printf ("imaginary\n"); - break; - case DEMANGLE_COMPONENT_VENDOR_TYPE: - printf ("vendor type\n"); - break; - case DEMANGLE_COMPONENT_FUNCTION_TYPE: - printf ("function type\n"); - break; - case DEMANGLE_COMPONENT_ARRAY_TYPE: - printf ("array type\n"); - break; - case DEMANGLE_COMPONENT_PTRMEM_TYPE: - printf ("pointer to member type\n"); - break; - case DEMANGLE_COMPONENT_ARGLIST: - printf ("argument list\n"); - break; - case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST: - printf ("template argument list\n"); - break; - case DEMANGLE_COMPONENT_CAST: - printf ("cast\n"); - break; - case DEMANGLE_COMPONENT_UNARY: - printf ("unary operator\n"); - break; - case DEMANGLE_COMPONENT_BINARY: - printf ("binary operator\n"); - break; - case DEMANGLE_COMPONENT_BINARY_ARGS: - printf ("binary operator arguments\n"); - break; - case DEMANGLE_COMPONENT_TRINARY: - printf ("trinary operator\n"); - break; - case DEMANGLE_COMPONENT_TRINARY_ARG1: - printf ("trinary operator arguments 1\n"); - break; - case DEMANGLE_COMPONENT_TRINARY_ARG2: - printf ("trinary operator arguments 1\n"); - break; - case DEMANGLE_COMPONENT_LITERAL: - printf ("literal\n"); - break; - case DEMANGLE_COMPONENT_LITERAL_NEG: - printf ("negative literal\n"); - break; - } - - d_dump (d_left (dc), indent + 2); - d_dump (d_right (dc), indent + 2); -} - -#endif /* CP_DEMANGLE_DEBUG */ - -/* Fill in a DEMANGLE_COMPONENT_NAME. */ - -CP_STATIC_IF_GLIBCPP_V3 -int -cplus_demangle_fill_name (p, s, len) - struct demangle_component *p; - const char *s; - int len; -{ - if (p == NULL || s == NULL || len == 0) - return 0; - p->type = DEMANGLE_COMPONENT_NAME; - p->u.s_name.s = s; - p->u.s_name.len = len; - return 1; -} - -/* Fill in a DEMANGLE_COMPONENT_EXTENDED_OPERATOR. */ - -CP_STATIC_IF_GLIBCPP_V3 -int -cplus_demangle_fill_extended_operator (p, args, name) - struct demangle_component *p; - int args; - struct demangle_component *name; -{ - if (p == NULL || args < 0 || name == NULL) - return 0; - p->type = DEMANGLE_COMPONENT_EXTENDED_OPERATOR; - p->u.s_extended_operator.args = args; - p->u.s_extended_operator.name = name; - return 1; -} - -/* Fill in a DEMANGLE_COMPONENT_CTOR. */ - -CP_STATIC_IF_GLIBCPP_V3 -int -cplus_demangle_fill_ctor (p, kind, name) - struct demangle_component *p; - enum gnu_v3_ctor_kinds kind; - struct demangle_component *name; -{ - if (p == NULL - || name == NULL - || (kind < gnu_v3_complete_object_ctor - && kind > gnu_v3_complete_object_allocating_ctor)) - return 0; - p->type = DEMANGLE_COMPONENT_CTOR; - p->u.s_ctor.kind = kind; - p->u.s_ctor.name = name; - return 1; -} - -/* Fill in a DEMANGLE_COMPONENT_DTOR. */ - -CP_STATIC_IF_GLIBCPP_V3 -int -cplus_demangle_fill_dtor (p, kind, name) - struct demangle_component *p; - enum gnu_v3_dtor_kinds kind; - struct demangle_component *name; -{ - if (p == NULL - || name == NULL - || (kind < gnu_v3_deleting_dtor - && kind > gnu_v3_base_object_dtor)) - return 0; - p->type = DEMANGLE_COMPONENT_DTOR; - p->u.s_dtor.kind = kind; - p->u.s_dtor.name = name; - return 1; -} - -/* Add a new component. */ - -static struct demangle_component * -d_make_empty (di) - struct d_info *di; -{ - struct demangle_component *p; - - if (di->next_comp >= di->num_comps) - return NULL; - p = &di->comps[di->next_comp]; - ++di->next_comp; - return p; -} - -/* Add a new generic component. */ - -static struct demangle_component * -d_make_comp (di, type, left, right) - struct d_info *di; - enum demangle_component_type type; - struct demangle_component *left; - struct demangle_component *right; -{ - struct demangle_component *p; - - /* We check for errors here. A typical error would be a NULL return - from a subroutine. We catch those here, and return NULL - upward. */ - switch (type) - { - /* These types require two parameters. */ - case DEMANGLE_COMPONENT_QUAL_NAME: - case DEMANGLE_COMPONENT_LOCAL_NAME: - case DEMANGLE_COMPONENT_TYPED_NAME: - case DEMANGLE_COMPONENT_TEMPLATE: - case DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE: - case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: - case DEMANGLE_COMPONENT_PTRMEM_TYPE: - case DEMANGLE_COMPONENT_UNARY: - case DEMANGLE_COMPONENT_BINARY: - case DEMANGLE_COMPONENT_BINARY_ARGS: - case DEMANGLE_COMPONENT_TRINARY: - case DEMANGLE_COMPONENT_TRINARY_ARG1: - case DEMANGLE_COMPONENT_TRINARY_ARG2: - case DEMANGLE_COMPONENT_LITERAL: - case DEMANGLE_COMPONENT_LITERAL_NEG: - if (left == NULL || right == NULL) - return NULL; - break; - - /* These types only require one parameter. */ - case DEMANGLE_COMPONENT_VTABLE: - case DEMANGLE_COMPONENT_VTT: - case DEMANGLE_COMPONENT_TYPEINFO: - case DEMANGLE_COMPONENT_TYPEINFO_NAME: - case DEMANGLE_COMPONENT_TYPEINFO_FN: - case DEMANGLE_COMPONENT_THUNK: - case DEMANGLE_COMPONENT_VIRTUAL_THUNK: - case DEMANGLE_COMPONENT_COVARIANT_THUNK: - case DEMANGLE_COMPONENT_JAVA_CLASS: - case DEMANGLE_COMPONENT_GUARD: - case DEMANGLE_COMPONENT_REFTEMP: - case DEMANGLE_COMPONENT_POINTER: - case DEMANGLE_COMPONENT_REFERENCE: - case DEMANGLE_COMPONENT_COMPLEX: - case DEMANGLE_COMPONENT_IMAGINARY: - case DEMANGLE_COMPONENT_VENDOR_TYPE: - case DEMANGLE_COMPONENT_ARGLIST: - case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST: - case DEMANGLE_COMPONENT_CAST: - if (left == NULL) - return NULL; - break; - - /* This needs a right parameter, but the left parameter can be - empty. */ - case DEMANGLE_COMPONENT_ARRAY_TYPE: - if (right == NULL) - return NULL; - break; - - /* These are allowed to have no parameters--in some cases they - will be filled in later. */ - case DEMANGLE_COMPONENT_FUNCTION_TYPE: - case DEMANGLE_COMPONENT_RESTRICT: - case DEMANGLE_COMPONENT_VOLATILE: - case DEMANGLE_COMPONENT_CONST: - case DEMANGLE_COMPONENT_RESTRICT_THIS: - case DEMANGLE_COMPONENT_VOLATILE_THIS: - case DEMANGLE_COMPONENT_CONST_THIS: - break; - - /* Other types should not be seen here. */ - default: - return NULL; - } - - p = d_make_empty (di); - if (p != NULL) - { - p->type = type; - p->u.s_binary.left = left; - p->u.s_binary.right = right; - } - return p; -} - -/* Add a new name component. */ - -static struct demangle_component * -d_make_name (di, s, len) - struct d_info *di; - const char *s; - int len; -{ - struct demangle_component *p; - - p = d_make_empty (di); - if (! cplus_demangle_fill_name (p, s, len)) - return NULL; - return p; -} - -/* Add a new builtin type component. */ - -static struct demangle_component * -d_make_builtin_type (di, type) - struct d_info *di; - const struct demangle_builtin_type_info *type; -{ - struct demangle_component *p; - - if (type == NULL) - return NULL; - p = d_make_empty (di); - if (p != NULL) - { - p->type = DEMANGLE_COMPONENT_BUILTIN_TYPE; - p->u.s_builtin.type = type; - } - return p; -} - -/* Add a new operator component. */ - -static struct demangle_component * -d_make_operator (di, op) - struct d_info *di; - const struct demangle_operator_info *op; -{ - struct demangle_component *p; - - p = d_make_empty (di); - if (p != NULL) - { - p->type = DEMANGLE_COMPONENT_OPERATOR; - p->u.s_operator.op = op; - } - return p; -} - -/* Add a new extended operator component. */ - -static struct demangle_component * -d_make_extended_operator (di, args, name) - struct d_info *di; - int args; - struct demangle_component *name; -{ - struct demangle_component *p; - - p = d_make_empty (di); - if (! cplus_demangle_fill_extended_operator (p, args, name)) - return NULL; - return p; -} - -/* Add a new constructor component. */ - -static struct demangle_component * -d_make_ctor (di, kind, name) - struct d_info *di; - enum gnu_v3_ctor_kinds kind; - struct demangle_component *name; -{ - struct demangle_component *p; - - p = d_make_empty (di); - if (! cplus_demangle_fill_ctor (p, kind, name)) - return NULL; - return p; -} - -/* Add a new destructor component. */ - -static struct demangle_component * -d_make_dtor (di, kind, name) - struct d_info *di; - enum gnu_v3_dtor_kinds kind; - struct demangle_component *name; -{ - struct demangle_component *p; - - p = d_make_empty (di); - if (! cplus_demangle_fill_dtor (p, kind, name)) - return NULL; - return p; -} - -/* Add a new template parameter. */ - -static struct demangle_component * -d_make_template_param (di, i) - struct d_info *di; - long i; -{ - struct demangle_component *p; - - p = d_make_empty (di); - if (p != NULL) - { - p->type = DEMANGLE_COMPONENT_TEMPLATE_PARAM; - p->u.s_number.number = i; - } - return p; -} - -/* Add a new standard substitution component. */ - -static struct demangle_component * -d_make_sub (di, name, len) - struct d_info *di; - const char *name; - int len; -{ - struct demangle_component *p; - - p = d_make_empty (di); - if (p != NULL) - { - p->type = DEMANGLE_COMPONENT_SUB_STD; - p->u.s_string.string = name; - p->u.s_string.len = len; - } - return p; -} - -/* ::= _Z - - TOP_LEVEL is non-zero when called at the top level. */ - -CP_STATIC_IF_GLIBCPP_V3 -struct demangle_component * -cplus_demangle_mangled_name (di, top_level) - struct d_info *di; - int top_level; -{ - if (d_next_char (di) != '_') - return NULL; - if (d_next_char (di) != 'Z') - return NULL; - return d_encoding (di, top_level); -} - -/* Return whether a function should have a return type. The argument - is the function name, which may be qualified in various ways. The - rules are that template functions have return types with some - exceptions, function types which are not part of a function name - mangling have return types with some exceptions, and non-template - function names do not have return types. The exceptions are that - constructors, destructors, and conversion operators do not have - return types. */ - -static int -has_return_type (dc) - struct demangle_component *dc; -{ - if (dc == NULL) - return 0; - switch (dc->type) - { - default: - return 0; - case DEMANGLE_COMPONENT_TEMPLATE: - return ! is_ctor_dtor_or_conversion (d_left (dc)); - case DEMANGLE_COMPONENT_RESTRICT_THIS: - case DEMANGLE_COMPONENT_VOLATILE_THIS: - case DEMANGLE_COMPONENT_CONST_THIS: - return has_return_type (d_left (dc)); - } -} - -/* Return whether a name is a constructor, a destructor, or a - conversion operator. */ - -static int -is_ctor_dtor_or_conversion (dc) - struct demangle_component *dc; -{ - if (dc == NULL) - return 0; - switch (dc->type) - { - default: - return 0; - case DEMANGLE_COMPONENT_QUAL_NAME: - case DEMANGLE_COMPONENT_LOCAL_NAME: - return is_ctor_dtor_or_conversion (d_right (dc)); - case DEMANGLE_COMPONENT_CTOR: - case DEMANGLE_COMPONENT_DTOR: - case DEMANGLE_COMPONENT_CAST: - return 1; - } -} - -/* ::= <(function) name> - ::= <(data) name> - ::= - - TOP_LEVEL is non-zero when called at the top level, in which case - if DMGL_PARAMS is not set we do not demangle the function - parameters. We only set this at the top level, because otherwise - we would not correctly demangle names in local scopes. */ - -static struct demangle_component * -d_encoding (di, top_level) - struct d_info *di; - int top_level; -{ - char peek = d_peek_char (di); - - if (peek == 'G' || peek == 'T') - return d_special_name (di); - else - { - struct demangle_component *dc; - - dc = d_name (di); - - if (dc != NULL && top_level && (di->options & DMGL_PARAMS) == 0) - { - /* Strip off any initial CV-qualifiers, as they really apply - to the `this' parameter, and they were not output by the - v2 demangler without DMGL_PARAMS. */ - while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS - || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS - || dc->type == DEMANGLE_COMPONENT_CONST_THIS) - dc = d_left (dc); - - /* If the top level is a DEMANGLE_COMPONENT_LOCAL_NAME, then - there may be CV-qualifiers on its right argument which - really apply here; this happens when parsing a class - which is local to a function. */ - if (dc->type == DEMANGLE_COMPONENT_LOCAL_NAME) - { - struct demangle_component *dcr; - - dcr = d_right (dc); - while (dcr->type == DEMANGLE_COMPONENT_RESTRICT_THIS - || dcr->type == DEMANGLE_COMPONENT_VOLATILE_THIS - || dcr->type == DEMANGLE_COMPONENT_CONST_THIS) - dcr = d_left (dcr); - dc->u.s_binary.right = dcr; - } - - return dc; - } - - peek = d_peek_char (di); - if (peek == '\0' || peek == 'E') - return dc; - return d_make_comp (di, DEMANGLE_COMPONENT_TYPED_NAME, dc, - d_bare_function_type (di, has_return_type (dc))); - } -} - -/* ::= - ::= - ::= - ::= - - ::= - ::= St - - ::= - ::= -*/ - -static struct demangle_component * -d_name (di) - struct d_info *di; -{ - char peek = d_peek_char (di); - struct demangle_component *dc; - - switch (peek) - { - case 'N': - return d_nested_name (di); - - case 'Z': - return d_local_name (di); - - case 'S': - { - int subst; - - if (d_peek_next_char (di) != 't') - { - dc = d_substitution (di, 0); - subst = 1; - } - else - { - d_advance (di, 2); - dc = d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, - d_make_name (di, "std", 3), - d_unqualified_name (di)); - di->expansion += 3; - subst = 0; - } - - if (d_peek_char (di) != 'I') - { - /* The grammar does not permit this case to occur if we - called d_substitution() above (i.e., subst == 1). We - don't bother to check. */ - } - else - { - /* This is , which means that we just saw - , which is a substitution - candidate if we didn't just get it from a - substitution. */ - if (! subst) - { - if (! d_add_substitution (di, dc)) - return NULL; - } - dc = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, dc, - d_template_args (di)); - } - - return dc; - } - - default: - dc = d_unqualified_name (di); - if (d_peek_char (di) == 'I') - { - /* This is , which means that we just saw - , which is a substitution - candidate. */ - if (! d_add_substitution (di, dc)) - return NULL; - dc = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, dc, - d_template_args (di)); - } - return dc; - } -} - -/* ::= N [] E - ::= N [] E -*/ - -static struct demangle_component * -d_nested_name (di) - struct d_info *di; -{ - struct demangle_component *ret; - struct demangle_component **pret; - - if (d_next_char (di) != 'N') - return NULL; - - pret = d_cv_qualifiers (di, &ret, 1); - if (pret == NULL) - return NULL; - - *pret = d_prefix (di); - if (*pret == NULL) - return NULL; - - if (d_next_char (di) != 'E') - return NULL; - - return ret; -} - -/* ::= - ::= - ::= - ::= - ::= - - ::= <(template) unqualified-name> - ::= - ::= -*/ - -static struct demangle_component * -d_prefix (di) - struct d_info *di; -{ - struct demangle_component *ret = NULL; - - while (1) - { - char peek; - enum demangle_component_type comb_type; - struct demangle_component *dc; - - peek = d_peek_char (di); - if (peek == '\0') - return NULL; - - /* The older code accepts a here, but I don't see - that in the grammar. The older code does not accept a - here. */ - - comb_type = DEMANGLE_COMPONENT_QUAL_NAME; - if (IS_DIGIT (peek) - || IS_LOWER (peek) - || peek == 'C' - || peek == 'D' - || peek == 'L') - dc = d_unqualified_name (di); - else if (peek == 'S') - dc = d_substitution (di, 1); - else if (peek == 'I') - { - if (ret == NULL) - return NULL; - comb_type = DEMANGLE_COMPONENT_TEMPLATE; - dc = d_template_args (di); - } - else if (peek == 'T') - dc = d_template_param (di); - else if (peek == 'E') - return ret; - else - return NULL; - - if (ret == NULL) - ret = dc; - else - ret = d_make_comp (di, comb_type, ret, dc); - - if (peek != 'S' && d_peek_char (di) != 'E') - { - if (! d_add_substitution (di, ret)) - return NULL; - } - } -} - -/* ::= - ::= - ::= - ::= - - ::= L -*/ - -static struct demangle_component * -d_unqualified_name (di) - struct d_info *di; -{ - char peek; - - peek = d_peek_char (di); - if (IS_DIGIT (peek)) - return d_source_name (di); - else if (IS_LOWER (peek)) - { - struct demangle_component *ret; - - ret = d_operator_name (di); - if (ret != NULL && ret->type == DEMANGLE_COMPONENT_OPERATOR) - di->expansion += sizeof "operator" + ret->u.s_operator.op->len - 2; - return ret; - } - else if (peek == 'C' || peek == 'D') - return d_ctor_dtor_name (di); - else if (peek == 'L') - { - struct demangle_component * ret; - - d_advance (di, 1); - - ret = d_source_name (di); - if (ret == NULL) - return NULL; - if (! d_discriminator (di)) - return NULL; - return ret; - } - else - return NULL; -} - -/* ::= <(positive length) number> */ - -static struct demangle_component * -d_source_name (di) - struct d_info *di; -{ - long len; - struct demangle_component *ret; - - len = d_number (di); - if (len <= 0) - return NULL; - ret = d_identifier (di, len); - di->last_name = ret; - return ret; -} - -/* number ::= [n] <(non-negative decimal integer)> */ - -static long -d_number (di) - struct d_info *di; -{ - int negative; - char peek; - long ret; - - negative = 0; - peek = d_peek_char (di); - if (peek == 'n') - { - negative = 1; - d_advance (di, 1); - peek = d_peek_char (di); - } - - ret = 0; - while (1) - { - if (! IS_DIGIT (peek)) - { - if (negative) - ret = - ret; - return ret; - } - ret = ret * 10 + peek - '0'; - d_advance (di, 1); - peek = d_peek_char (di); - } -} - -/* identifier ::= <(unqualified source code identifier)> */ - -static struct demangle_component * -d_identifier (di, len) - struct d_info *di; - int len; -{ - const char *name; - - name = d_str (di); - - if (di->send - name < len) - return NULL; - - d_advance (di, len); - - /* A Java mangled name may have a trailing '$' if it is a C++ - keyword. This '$' is not included in the length count. We just - ignore the '$'. */ - if ((di->options & DMGL_JAVA) != 0 - && d_peek_char (di) == '$') - d_advance (di, 1); - - /* Look for something which looks like a gcc encoding of an - anonymous namespace, and replace it with a more user friendly - name. */ - if (len >= (int) ANONYMOUS_NAMESPACE_PREFIX_LEN + 2 - && memcmp (name, ANONYMOUS_NAMESPACE_PREFIX, - ANONYMOUS_NAMESPACE_PREFIX_LEN) == 0) - { - const char *s; - - s = name + ANONYMOUS_NAMESPACE_PREFIX_LEN; - if ((*s == '.' || *s == '_' || *s == '$') - && s[1] == 'N') - { - di->expansion -= len - sizeof "(anonymous namespace)"; - return d_make_name (di, "(anonymous namespace)", - sizeof "(anonymous namespace)" - 1); - } - } - - return d_make_name (di, name, len); -} - -/* operator_name ::= many different two character encodings. - ::= cv - ::= v -*/ - -#define NL(s) s, (sizeof s) - 1 - -CP_STATIC_IF_GLIBCPP_V3 -const struct demangle_operator_info cplus_demangle_operators[] = -{ - { "aN", NL ("&="), 2 }, - { "aS", NL ("="), 2 }, - { "aa", NL ("&&"), 2 }, - { "ad", NL ("&"), 1 }, - { "an", NL ("&"), 2 }, - { "cl", NL ("()"), 0 }, - { "cm", NL (","), 2 }, - { "co", NL ("~"), 1 }, - { "dV", NL ("/="), 2 }, - { "da", NL ("delete[]"), 1 }, - { "de", NL ("*"), 1 }, - { "dl", NL ("delete"), 1 }, - { "dv", NL ("/"), 2 }, - { "eO", NL ("^="), 2 }, - { "eo", NL ("^"), 2 }, - { "eq", NL ("=="), 2 }, - { "ge", NL (">="), 2 }, - { "gt", NL (">"), 2 }, - { "ix", NL ("[]"), 2 }, - { "lS", NL ("<<="), 2 }, - { "le", NL ("<="), 2 }, - { "ls", NL ("<<"), 2 }, - { "lt", NL ("<"), 2 }, - { "mI", NL ("-="), 2 }, - { "mL", NL ("*="), 2 }, - { "mi", NL ("-"), 2 }, - { "ml", NL ("*"), 2 }, - { "mm", NL ("--"), 1 }, - { "na", NL ("new[]"), 1 }, - { "ne", NL ("!="), 2 }, - { "ng", NL ("-"), 1 }, - { "nt", NL ("!"), 1 }, - { "nw", NL ("new"), 1 }, - { "oR", NL ("|="), 2 }, - { "oo", NL ("||"), 2 }, - { "or", NL ("|"), 2 }, - { "pL", NL ("+="), 2 }, - { "pl", NL ("+"), 2 }, - { "pm", NL ("->*"), 2 }, - { "pp", NL ("++"), 1 }, - { "ps", NL ("+"), 1 }, - { "pt", NL ("->"), 2 }, - { "qu", NL ("?"), 3 }, - { "rM", NL ("%="), 2 }, - { "rS", NL (">>="), 2 }, - { "rm", NL ("%"), 2 }, - { "rs", NL (">>"), 2 }, - { "st", NL ("sizeof "), 1 }, - { "sz", NL ("sizeof "), 1 }, - { NULL, NULL, 0, 0 } -}; - -static struct demangle_component * -d_operator_name (di) - struct d_info *di; -{ - char c1; - char c2; - - c1 = d_next_char (di); - c2 = d_next_char (di); - if (c1 == 'v' && IS_DIGIT (c2)) - return d_make_extended_operator (di, c2 - '0', d_source_name (di)); - else if (c1 == 'c' && c2 == 'v') - return d_make_comp (di, DEMANGLE_COMPONENT_CAST, - cplus_demangle_type (di), NULL); - else - { - /* LOW is the inclusive lower bound. */ - int low = 0; - /* HIGH is the exclusive upper bound. We subtract one to ignore - the sentinel at the end of the array. */ - int high = ((sizeof (cplus_demangle_operators) - / sizeof (cplus_demangle_operators[0])) - - 1); - - while (1) - { - int i; - const struct demangle_operator_info *p; - - i = low + (high - low) / 2; - p = cplus_demangle_operators + i; - - if (c1 == p->code[0] && c2 == p->code[1]) - return d_make_operator (di, p); - - if (c1 < p->code[0] || (c1 == p->code[0] && c2 < p->code[1])) - high = i; - else - low = i + 1; - if (low == high) - return NULL; - } - } -} - -/* ::= TV - ::= TT - ::= TI - ::= TS - ::= GV <(object) name> - ::= T <(base) encoding> - ::= Tc <(base) encoding> - Also g++ extensions: - ::= TC <(offset) number> _ <(base) type> - ::= TF - ::= TJ - ::= GR -*/ - -static struct demangle_component * -d_special_name (di) - struct d_info *di; -{ - char c; - - di->expansion += 20; - c = d_next_char (di); - if (c == 'T') - { - switch (d_next_char (di)) - { - case 'V': - di->expansion -= 5; - return d_make_comp (di, DEMANGLE_COMPONENT_VTABLE, - cplus_demangle_type (di), NULL); - case 'T': - di->expansion -= 10; - return d_make_comp (di, DEMANGLE_COMPONENT_VTT, - cplus_demangle_type (di), NULL); - case 'I': - return d_make_comp (di, DEMANGLE_COMPONENT_TYPEINFO, - cplus_demangle_type (di), NULL); - case 'S': - return d_make_comp (di, DEMANGLE_COMPONENT_TYPEINFO_NAME, - cplus_demangle_type (di), NULL); - - case 'h': - if (! d_call_offset (di, 'h')) - return NULL; - return d_make_comp (di, DEMANGLE_COMPONENT_THUNK, - d_encoding (di, 0), NULL); - - case 'v': - if (! d_call_offset (di, 'v')) - return NULL; - return d_make_comp (di, DEMANGLE_COMPONENT_VIRTUAL_THUNK, - d_encoding (di, 0), NULL); - - case 'c': - if (! d_call_offset (di, '\0')) - return NULL; - if (! d_call_offset (di, '\0')) - return NULL; - return d_make_comp (di, DEMANGLE_COMPONENT_COVARIANT_THUNK, - d_encoding (di, 0), NULL); - - case 'C': - { - struct demangle_component *derived_type; - long offset; - struct demangle_component *base_type; - - derived_type = cplus_demangle_type (di); - offset = d_number (di); - if (offset < 0) - return NULL; - if (d_next_char (di) != '_') - return NULL; - base_type = cplus_demangle_type (di); - /* We don't display the offset. FIXME: We should display - it in verbose mode. */ - di->expansion += 5; - return d_make_comp (di, DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE, - base_type, derived_type); - } - - case 'F': - return d_make_comp (di, DEMANGLE_COMPONENT_TYPEINFO_FN, - cplus_demangle_type (di), NULL); - case 'J': - return d_make_comp (di, DEMANGLE_COMPONENT_JAVA_CLASS, - cplus_demangle_type (di), NULL); - - default: - return NULL; - } - } - else if (c == 'G') - { - switch (d_next_char (di)) - { - case 'V': - return d_make_comp (di, DEMANGLE_COMPONENT_GUARD, d_name (di), NULL); - - case 'R': - return d_make_comp (di, DEMANGLE_COMPONENT_REFTEMP, d_name (di), - NULL); - - default: - return NULL; - } - } - else - return NULL; -} - -/* ::= h _ - ::= v _ - - ::= <(offset) number> - - ::= <(offset) number> _ <(virtual offset) number> - - The C parameter, if not '\0', is a character we just read which is - the start of the . - - We don't display the offset information anywhere. FIXME: We should - display it in verbose mode. */ - -static int -d_call_offset (di, c) - struct d_info *di; - int c; -{ - if (c == '\0') - c = d_next_char (di); - - if (c == 'h') - d_number (di); - else if (c == 'v') - { - d_number (di); - if (d_next_char (di) != '_') - return 0; - d_number (di); - } - else - return 0; - - if (d_next_char (di) != '_') - return 0; - - return 1; -} - -/* ::= C1 - ::= C2 - ::= C3 - ::= D0 - ::= D1 - ::= D2 -*/ - -static struct demangle_component * -d_ctor_dtor_name (di) - struct d_info *di; -{ - if (di->last_name != NULL) - { - if (di->last_name->type == DEMANGLE_COMPONENT_NAME) - di->expansion += di->last_name->u.s_name.len; - else if (di->last_name->type == DEMANGLE_COMPONENT_SUB_STD) - di->expansion += di->last_name->u.s_string.len; - } - switch (d_next_char (di)) - { - case 'C': - { - enum gnu_v3_ctor_kinds kind; - - switch (d_next_char (di)) - { - case '1': - kind = gnu_v3_complete_object_ctor; - break; - case '2': - kind = gnu_v3_base_object_ctor; - break; - case '3': - kind = gnu_v3_complete_object_allocating_ctor; - break; - default: - return NULL; - } - return d_make_ctor (di, kind, di->last_name); - } - - case 'D': - { - enum gnu_v3_dtor_kinds kind; - - switch (d_next_char (di)) - { - case '0': - kind = gnu_v3_deleting_dtor; - break; - case '1': - kind = gnu_v3_complete_object_dtor; - break; - case '2': - kind = gnu_v3_base_object_dtor; - break; - default: - return NULL; - } - return d_make_dtor (di, kind, di->last_name); - } - - default: - return NULL; - } -} - -/* ::= - ::= - ::= - ::= - ::= - ::= - ::= - ::= - ::= - ::= P - ::= R - ::= C - ::= G - ::= U - - ::= various one letter codes - ::= u -*/ - -CP_STATIC_IF_GLIBCPP_V3 -const struct demangle_builtin_type_info -cplus_demangle_builtin_types[D_BUILTIN_TYPE_COUNT] = -{ - /* a */ { NL ("signed char"), NL ("signed char"), D_PRINT_DEFAULT }, - /* b */ { NL ("bool"), NL ("boolean"), D_PRINT_BOOL }, - /* c */ { NL ("char"), NL ("byte"), D_PRINT_DEFAULT }, - /* d */ { NL ("double"), NL ("double"), D_PRINT_FLOAT }, - /* e */ { NL ("long double"), NL ("long double"), D_PRINT_FLOAT }, - /* f */ { NL ("float"), NL ("float"), D_PRINT_FLOAT }, - /* g */ { NL ("__float128"), NL ("__float128"), D_PRINT_FLOAT }, - /* h */ { NL ("unsigned char"), NL ("unsigned char"), D_PRINT_DEFAULT }, - /* i */ { NL ("int"), NL ("int"), D_PRINT_INT }, - /* j */ { NL ("unsigned int"), NL ("unsigned"), D_PRINT_UNSIGNED }, - /* k */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT }, - /* l */ { NL ("long"), NL ("long"), D_PRINT_LONG }, - /* m */ { NL ("unsigned long"), NL ("unsigned long"), D_PRINT_UNSIGNED_LONG }, - /* n */ { NL ("__int128"), NL ("__int128"), D_PRINT_DEFAULT }, - /* o */ { NL ("unsigned __int128"), NL ("unsigned __int128"), - D_PRINT_DEFAULT }, - /* p */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT }, - /* q */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT }, - /* r */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT }, - /* s */ { NL ("short"), NL ("short"), D_PRINT_DEFAULT }, - /* t */ { NL ("unsigned short"), NL ("unsigned short"), D_PRINT_DEFAULT }, - /* u */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT }, - /* v */ { NL ("void"), NL ("void"), D_PRINT_VOID }, - /* w */ { NL ("wchar_t"), NL ("char"), D_PRINT_DEFAULT }, - /* x */ { NL ("long long"), NL ("long"), D_PRINT_LONG_LONG }, - /* y */ { NL ("unsigned long long"), NL ("unsigned long long"), - D_PRINT_UNSIGNED_LONG_LONG }, - /* z */ { NL ("..."), NL ("..."), D_PRINT_DEFAULT }, -}; - -CP_STATIC_IF_GLIBCPP_V3 -struct demangle_component * -cplus_demangle_type (di) - struct d_info *di; -{ - char peek; - struct demangle_component *ret; - int can_subst; - - /* The ABI specifies that when CV-qualifiers are used, the base type - is substitutable, and the fully qualified type is substitutable, - but the base type with a strict subset of the CV-qualifiers is - not substitutable. The natural recursive implementation of the - CV-qualifiers would cause subsets to be substitutable, so instead - we pull them all off now. - - FIXME: The ABI says that order-insensitive vendor qualifiers - should be handled in the same way, but we have no way to tell - which vendor qualifiers are order-insensitive and which are - order-sensitive. So we just assume that they are all - order-sensitive. g++ 3.4 supports only one vendor qualifier, - __vector, and it treats it as order-sensitive when mangling - names. */ - - peek = d_peek_char (di); - if (peek == 'r' || peek == 'V' || peek == 'K') - { - struct demangle_component **pret; - - pret = d_cv_qualifiers (di, &ret, 0); - if (pret == NULL) - return NULL; - *pret = cplus_demangle_type (di); - if (! d_add_substitution (di, ret)) - return NULL; - return ret; - } - - can_subst = 1; - - switch (peek) - { - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': - case 'h': case 'i': case 'j': case 'l': case 'm': case 'n': - case 'o': case 's': case 't': - case 'v': case 'w': case 'x': case 'y': case 'z': - ret = d_make_builtin_type (di, - &cplus_demangle_builtin_types[peek - 'a']); - di->expansion += ret->u.s_builtin.type->len; - can_subst = 0; - d_advance (di, 1); - break; - - case 'u': - d_advance (di, 1); - ret = d_make_comp (di, DEMANGLE_COMPONENT_VENDOR_TYPE, - d_source_name (di), NULL); - break; - - case 'F': - ret = d_function_type (di); - break; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case 'N': - case 'Z': - ret = d_class_enum_type (di); - break; - - case 'A': - ret = d_array_type (di); - break; - - case 'M': - ret = d_pointer_to_member_type (di); - break; - - case 'T': - ret = d_template_param (di); - if (d_peek_char (di) == 'I') - { - /* This is . The - part is a substitution - candidate. */ - if (! d_add_substitution (di, ret)) - return NULL; - ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret, - d_template_args (di)); - } - break; - - case 'S': - /* If this is a special substitution, then it is the start of - . */ - { - char peek_next; - - peek_next = d_peek_next_char (di); - if (IS_DIGIT (peek_next) - || peek_next == '_' - || IS_UPPER (peek_next)) - { - ret = d_substitution (di, 0); - /* The substituted name may have been a template name and - may be followed by tepmlate args. */ - if (d_peek_char (di) == 'I') - ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret, - d_template_args (di)); - else - can_subst = 0; - } - else - { - ret = d_class_enum_type (di); - /* If the substitution was a complete type, then it is not - a new substitution candidate. However, if the - substitution was followed by template arguments, then - the whole thing is a substitution candidate. */ - if (ret != NULL && ret->type == DEMANGLE_COMPONENT_SUB_STD) - can_subst = 0; - } - } - break; - - case 'P': - d_advance (di, 1); - ret = d_make_comp (di, DEMANGLE_COMPONENT_POINTER, - cplus_demangle_type (di), NULL); - break; - - case 'R': - d_advance (di, 1); - ret = d_make_comp (di, DEMANGLE_COMPONENT_REFERENCE, - cplus_demangle_type (di), NULL); - break; - - case 'C': - d_advance (di, 1); - ret = d_make_comp (di, DEMANGLE_COMPONENT_COMPLEX, - cplus_demangle_type (di), NULL); - break; - - case 'G': - d_advance (di, 1); - ret = d_make_comp (di, DEMANGLE_COMPONENT_IMAGINARY, - cplus_demangle_type (di), NULL); - break; - - case 'U': - d_advance (di, 1); - ret = d_source_name (di); - ret = d_make_comp (di, DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL, - cplus_demangle_type (di), ret); - break; - - default: - return NULL; - } - - if (can_subst) - { - if (! d_add_substitution (di, ret)) - return NULL; - } - - return ret; -} - -/* ::= [r] [V] [K] */ - -static struct demangle_component ** -d_cv_qualifiers (di, pret, member_fn) - struct d_info *di; - struct demangle_component **pret; - int member_fn; -{ - char peek; - - peek = d_peek_char (di); - while (peek == 'r' || peek == 'V' || peek == 'K') - { - enum demangle_component_type t; - - d_advance (di, 1); - if (peek == 'r') - { - t = (member_fn - ? DEMANGLE_COMPONENT_RESTRICT_THIS - : DEMANGLE_COMPONENT_RESTRICT); - di->expansion += sizeof "restrict"; - } - else if (peek == 'V') - { - t = (member_fn - ? DEMANGLE_COMPONENT_VOLATILE_THIS - : DEMANGLE_COMPONENT_VOLATILE); - di->expansion += sizeof "volatile"; - } - else - { - t = (member_fn - ? DEMANGLE_COMPONENT_CONST_THIS - : DEMANGLE_COMPONENT_CONST); - di->expansion += sizeof "const"; - } - - *pret = d_make_comp (di, t, NULL, NULL); - if (*pret == NULL) - return NULL; - pret = &d_left (*pret); - - peek = d_peek_char (di); - } - - return pret; -} - -/* ::= F [Y] E */ - -static struct demangle_component * -d_function_type (di) - struct d_info *di; -{ - struct demangle_component *ret; - - if (d_next_char (di) != 'F') - return NULL; - if (d_peek_char (di) == 'Y') - { - /* Function has C linkage. We don't print this information. - FIXME: We should print it in verbose mode. */ - d_advance (di, 1); - } - ret = d_bare_function_type (di, 1); - if (d_next_char (di) != 'E') - return NULL; - return ret; -} - -/* ::= + */ - -static struct demangle_component * -d_bare_function_type (di, has_return_type) - struct d_info *di; - int has_return_type; -{ - struct demangle_component *return_type; - struct demangle_component *tl; - struct demangle_component **ptl; - - return_type = NULL; - tl = NULL; - ptl = &tl; - while (1) - { - char peek; - struct demangle_component *type; - - peek = d_peek_char (di); - if (peek == '\0' || peek == 'E') - break; - type = cplus_demangle_type (di); - if (type == NULL) - return NULL; - if (has_return_type) - { - return_type = type; - has_return_type = 0; - } - else - { - *ptl = d_make_comp (di, DEMANGLE_COMPONENT_ARGLIST, type, NULL); - if (*ptl == NULL) - return NULL; - ptl = &d_right (*ptl); - } - } - - /* There should be at least one parameter type besides the optional - return type. A function which takes no arguments will have a - single parameter type void. */ - if (tl == NULL) - return NULL; - - /* If we have a single parameter type void, omit it. */ - if (d_right (tl) == NULL - && d_left (tl)->type == DEMANGLE_COMPONENT_BUILTIN_TYPE - && d_left (tl)->u.s_builtin.type->print == D_PRINT_VOID) - { - di->expansion -= d_left (tl)->u.s_builtin.type->len; - tl = NULL; - } - - return d_make_comp (di, DEMANGLE_COMPONENT_FUNCTION_TYPE, return_type, tl); -} - -/* ::= */ - -static struct demangle_component * -d_class_enum_type (di) - struct d_info *di; -{ - return d_name (di); -} - -/* ::= A <(positive dimension) number> _ <(element) type> - ::= A [<(dimension) expression>] _ <(element) type> -*/ - -static struct demangle_component * -d_array_type (di) - struct d_info *di; -{ - char peek; - struct demangle_component *dim; - - if (d_next_char (di) != 'A') - return NULL; - - peek = d_peek_char (di); - if (peek == '_') - dim = NULL; - else if (IS_DIGIT (peek)) - { - const char *s; - - s = d_str (di); - do - { - d_advance (di, 1); - peek = d_peek_char (di); - } - while (IS_DIGIT (peek)); - dim = d_make_name (di, s, d_str (di) - s); - if (dim == NULL) - return NULL; - } - else - { - dim = d_expression (di); - if (dim == NULL) - return NULL; - } - - if (d_next_char (di) != '_') - return NULL; - - return d_make_comp (di, DEMANGLE_COMPONENT_ARRAY_TYPE, dim, - cplus_demangle_type (di)); -} - -/* ::= M <(class) type> <(member) type> */ - -static struct demangle_component * -d_pointer_to_member_type (di) - struct d_info *di; -{ - struct demangle_component *cl; - struct demangle_component *mem; - struct demangle_component **pmem; - - if (d_next_char (di) != 'M') - return NULL; - - cl = cplus_demangle_type (di); - - /* The ABI specifies that any type can be a substitution source, and - that M is followed by two types, and that when a CV-qualified - type is seen both the base type and the CV-qualified types are - substitution sources. The ABI also specifies that for a pointer - to a CV-qualified member function, the qualifiers are attached to - the second type. Given the grammar, a plain reading of the ABI - suggests that both the CV-qualified member function and the - non-qualified member function are substitution sources. However, - g++ does not work that way. g++ treats only the CV-qualified - member function as a substitution source. FIXME. So to work - with g++, we need to pull off the CV-qualifiers here, in order to - avoid calling add_substitution() in cplus_demangle_type(). */ - - pmem = d_cv_qualifiers (di, &mem, 1); - if (pmem == NULL) - return NULL; - *pmem = cplus_demangle_type (di); - - return d_make_comp (di, DEMANGLE_COMPONENT_PTRMEM_TYPE, cl, mem); -} - -/* ::= T_ - ::= T <(parameter-2 non-negative) number> _ -*/ - -static struct demangle_component * -d_template_param (di) - struct d_info *di; -{ - long param; - - if (d_next_char (di) != 'T') - return NULL; - - if (d_peek_char (di) == '_') - param = 0; - else - { - param = d_number (di); - if (param < 0) - return NULL; - param += 1; - } - - if (d_next_char (di) != '_') - return NULL; - - ++di->did_subs; - - return d_make_template_param (di, param); -} - -/* ::= I + E */ - -static struct demangle_component * -d_template_args (di) - struct d_info *di; -{ - struct demangle_component *hold_last_name; - struct demangle_component *al; - struct demangle_component **pal; - - /* Preserve the last name we saw--don't let the template arguments - clobber it, as that would give us the wrong name for a subsequent - constructor or destructor. */ - hold_last_name = di->last_name; - - if (d_next_char (di) != 'I') - return NULL; - - al = NULL; - pal = &al; - while (1) - { - struct demangle_component *a; - - a = d_template_arg (di); - if (a == NULL) - return NULL; - - *pal = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE_ARGLIST, a, NULL); - if (*pal == NULL) - return NULL; - pal = &d_right (*pal); - - if (d_peek_char (di) == 'E') - { - d_advance (di, 1); - break; - } - } - - di->last_name = hold_last_name; - - return al; -} - -/* ::= - ::= X E - ::= -*/ - -static struct demangle_component * -d_template_arg (di) - struct d_info *di; -{ - struct demangle_component *ret; - - switch (d_peek_char (di)) - { - case 'X': - d_advance (di, 1); - ret = d_expression (di); - if (d_next_char (di) != 'E') - return NULL; - return ret; - - case 'L': - return d_expr_primary (di); - - default: - return cplus_demangle_type (di); - } -} - -/* ::= <(unary) operator-name> - ::= <(binary) operator-name> - ::= <(trinary) operator-name> - ::= st - ::= - ::= sr - ::= sr - ::= -*/ - -static struct demangle_component * -d_expression (di) - struct d_info *di; -{ - char peek; - - peek = d_peek_char (di); - if (peek == 'L') - return d_expr_primary (di); - else if (peek == 'T') - return d_template_param (di); - else if (peek == 's' && d_peek_next_char (di) == 'r') - { - struct demangle_component *type; - struct demangle_component *name; - - d_advance (di, 2); - type = cplus_demangle_type (di); - name = d_unqualified_name (di); - if (d_peek_char (di) != 'I') - return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type, name); - else - return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type, - d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, name, - d_template_args (di))); - } - else - { - struct demangle_component *op; - int args; - - op = d_operator_name (di); - if (op == NULL) - return NULL; - - if (op->type == DEMANGLE_COMPONENT_OPERATOR) - di->expansion += op->u.s_operator.op->len - 2; - - if (op->type == DEMANGLE_COMPONENT_OPERATOR - && strcmp (op->u.s_operator.op->code, "st") == 0) - return d_make_comp (di, DEMANGLE_COMPONENT_UNARY, op, - cplus_demangle_type (di)); - - switch (op->type) - { - default: - return NULL; - case DEMANGLE_COMPONENT_OPERATOR: - args = op->u.s_operator.op->args; - break; - case DEMANGLE_COMPONENT_EXTENDED_OPERATOR: - args = op->u.s_extended_operator.args; - break; - case DEMANGLE_COMPONENT_CAST: - args = 1; - break; - } - - switch (args) - { - case 1: - return d_make_comp (di, DEMANGLE_COMPONENT_UNARY, op, - d_expression (di)); - case 2: - { - struct demangle_component *left; - - left = d_expression (di); - return d_make_comp (di, DEMANGLE_COMPONENT_BINARY, op, - d_make_comp (di, - DEMANGLE_COMPONENT_BINARY_ARGS, - left, - d_expression (di))); - } - case 3: - { - struct demangle_component *first; - struct demangle_component *second; - - first = d_expression (di); - second = d_expression (di); - return d_make_comp (di, DEMANGLE_COMPONENT_TRINARY, op, - d_make_comp (di, - DEMANGLE_COMPONENT_TRINARY_ARG1, - first, - d_make_comp (di, - DEMANGLE_COMPONENT_TRINARY_ARG2, - second, - d_expression (di)))); - } - default: - return NULL; - } - } -} - -/* ::= L <(value) number> E - ::= L <(value) float> E - ::= L E -*/ - -static struct demangle_component * -d_expr_primary (di) - struct d_info *di; -{ - struct demangle_component *ret; - - if (d_next_char (di) != 'L') - return NULL; - if (d_peek_char (di) == '_') - ret = cplus_demangle_mangled_name (di, 0); - else - { - struct demangle_component *type; - enum demangle_component_type t; - const char *s; - - type = cplus_demangle_type (di); - if (type == NULL) - return NULL; - - /* If we have a type we know how to print, we aren't going to - print the type name itself. */ - if (type->type == DEMANGLE_COMPONENT_BUILTIN_TYPE - && type->u.s_builtin.type->print != D_PRINT_DEFAULT) - di->expansion -= type->u.s_builtin.type->len; - - /* Rather than try to interpret the literal value, we just - collect it as a string. Note that it's possible to have a - floating point literal here. The ABI specifies that the - format of such literals is machine independent. That's fine, - but what's not fine is that versions of g++ up to 3.2 with - -fabi-version=1 used upper case letters in the hex constant, - and dumped out gcc's internal representation. That makes it - hard to tell where the constant ends, and hard to dump the - constant in any readable form anyhow. We don't attempt to - handle these cases. */ - - t = DEMANGLE_COMPONENT_LITERAL; - if (d_peek_char (di) == 'n') - { - t = DEMANGLE_COMPONENT_LITERAL_NEG; - d_advance (di, 1); - } - s = d_str (di); - while (d_peek_char (di) != 'E') - d_advance (di, 1); - ret = d_make_comp (di, t, type, d_make_name (di, s, d_str (di) - s)); - } - if (d_next_char (di) != 'E') - return NULL; - return ret; -} - -/* ::= Z <(function) encoding> E <(entity) name> [] - ::= Z <(function) encoding> E s [] -*/ - -static struct demangle_component * -d_local_name (di) - struct d_info *di; -{ - struct demangle_component *function; - - if (d_next_char (di) != 'Z') - return NULL; - - function = d_encoding (di, 0); - - if (d_next_char (di) != 'E') - return NULL; - - if (d_peek_char (di) == 's') - { - d_advance (di, 1); - if (! d_discriminator (di)) - return NULL; - return d_make_comp (di, DEMANGLE_COMPONENT_LOCAL_NAME, function, - d_make_name (di, "string literal", - sizeof "string literal" - 1)); - } - else - { - struct demangle_component *name; - - name = d_name (di); - if (! d_discriminator (di)) - return NULL; - return d_make_comp (di, DEMANGLE_COMPONENT_LOCAL_NAME, function, name); - } -} - -/* ::= _ <(non-negative) number> - - We demangle the discriminator, but we don't print it out. FIXME: - We should print it out in verbose mode. */ - -static int -d_discriminator (di) - struct d_info *di; -{ - long discrim; - - if (d_peek_char (di) != '_') - return 1; - d_advance (di, 1); - discrim = d_number (di); - if (discrim < 0) - return 0; - return 1; -} - -/* Add a new substitution. */ - -static int -d_add_substitution (di, dc) - struct d_info *di; - struct demangle_component *dc; -{ - if (dc == NULL) - return 0; - if (di->next_sub >= di->num_subs) - return 0; - di->subs[di->next_sub] = dc; - ++di->next_sub; - return 1; -} - -/* ::= S _ - ::= S_ - ::= St - ::= Sa - ::= Sb - ::= Ss - ::= Si - ::= So - ::= Sd - - If PREFIX is non-zero, then this type is being used as a prefix in - a qualified name. In this case, for the standard substitutions, we - need to check whether we are being used as a prefix for a - constructor or destructor, and return a full template name. - Otherwise we will get something like std::iostream::~iostream() - which does not correspond particularly well to any function which - actually appears in the source. -*/ - -static const struct d_standard_sub_info standard_subs[] = -{ - { 't', NL ("std"), - NL ("std"), - NULL, 0 }, - { 'a', NL ("std::allocator"), - NL ("std::allocator"), - NL ("allocator") }, - { 'b', NL ("std::basic_string"), - NL ("std::basic_string"), - NL ("basic_string") }, - { 's', NL ("std::string"), - NL ("std::basic_string, std::allocator >"), - NL ("basic_string") }, - { 'i', NL ("std::istream"), - NL ("std::basic_istream >"), - NL ("basic_istream") }, - { 'o', NL ("std::ostream"), - NL ("std::basic_ostream >"), - NL ("basic_ostream") }, - { 'd', NL ("std::iostream"), - NL ("std::basic_iostream >"), - NL ("basic_iostream") } -}; - -static struct demangle_component * -d_substitution (di, prefix) - struct d_info *di; - int prefix; -{ - char c; - - if (d_next_char (di) != 'S') - return NULL; - - c = d_next_char (di); - if (c == '_' || IS_DIGIT (c) || IS_UPPER (c)) - { - int id; - - id = 0; - if (c != '_') - { - do - { - if (IS_DIGIT (c)) - id = id * 36 + c - '0'; - else if (IS_UPPER (c)) - id = id * 36 + c - 'A' + 10; - else - return NULL; - c = d_next_char (di); - } - while (c != '_'); - - ++id; - } - - if (id >= di->next_sub) - return NULL; - - ++di->did_subs; - - return di->subs[id]; - } - else - { - int verbose; - const struct d_standard_sub_info *p; - const struct d_standard_sub_info *pend; - - verbose = (di->options & DMGL_VERBOSE) != 0; - if (! verbose && prefix) - { - char peek; - - peek = d_peek_char (di); - if (peek == 'C' || peek == 'D') - verbose = 1; - } - - pend = (&standard_subs[0] - + sizeof standard_subs / sizeof standard_subs[0]); - for (p = &standard_subs[0]; p < pend; ++p) - { - if (c == p->code) - { - const char *s; - int len; - - if (p->set_last_name != NULL) - di->last_name = d_make_sub (di, p->set_last_name, - p->set_last_name_len); - if (verbose) - { - s = p->full_expansion; - len = p->full_len; - } - else - { - s = p->simple_expansion; - len = p->simple_len; - } - di->expansion += len; - return d_make_sub (di, s, len); - } - } - - return NULL; - } -} - -/* Resize the print buffer. */ - -static void -d_print_resize (dpi, add) - struct d_print_info *dpi; - size_t add; -{ - size_t need; - - if (dpi->buf == NULL) - return; - need = dpi->len + add; - while (need > dpi->alc) - { - size_t newalc; - char *newbuf; - - newalc = dpi->alc * 2; - newbuf = realloc (dpi->buf, newalc); - if (newbuf == NULL) - { - free (dpi->buf); - dpi->buf = NULL; - dpi->allocation_failure = 1; - return; - } - dpi->buf = newbuf; - dpi->alc = newalc; - } -} - -/* Append a character to the print buffer. */ - -static void -d_print_append_char (dpi, c) - struct d_print_info *dpi; - int c; -{ - if (dpi->buf != NULL) - { - if (dpi->len >= dpi->alc) - { - d_print_resize (dpi, 1); - if (dpi->buf == NULL) - return; - } - - dpi->buf[dpi->len] = c; - ++dpi->len; - } -} - -/* Append a buffer to the print buffer. */ - -static void -d_print_append_buffer (dpi, s, l) - struct d_print_info *dpi; - const char *s; - size_t l; -{ - if (dpi->buf != NULL) - { - if (dpi->len + l > dpi->alc) - { - d_print_resize (dpi, l); - if (dpi->buf == NULL) - return; - } - - memcpy (dpi->buf + dpi->len, s, l); - dpi->len += l; - } -} - -/* Indicate that an error occurred during printing. */ - -static void -d_print_error (dpi) - struct d_print_info *dpi; -{ - free (dpi->buf); - dpi->buf = NULL; -} - -/* Turn components into a human readable string. OPTIONS is the - options bits passed to the demangler. DC is the tree to print. - ESTIMATE is a guess at the length of the result. This returns a - string allocated by malloc, or NULL on error. On success, this - sets *PALC to the size of the allocated buffer. On failure, this - sets *PALC to 0 for a bad parse, or to 1 for a memory allocation - failure. */ - -CP_STATIC_IF_GLIBCPP_V3 -char * -cplus_demangle_print (options, dc, estimate, palc) - int options; - const struct demangle_component *dc; - int estimate; - size_t *palc; -{ - struct d_print_info dpi; - - dpi.options = options; - - dpi.alc = estimate + 1; - dpi.buf = malloc (dpi.alc); - if (dpi.buf == NULL) - { - *palc = 1; - return NULL; - } - - dpi.len = 0; - dpi.templates = NULL; - dpi.modifiers = NULL; - - dpi.allocation_failure = 0; - - d_print_comp (&dpi, dc); - - d_append_char (&dpi, '\0'); - - if (dpi.buf != NULL) - *palc = dpi.alc; - else - *palc = dpi.allocation_failure; - - return dpi.buf; -} - -/* Subroutine to handle components. */ - -static void -d_print_comp (dpi, dc) - struct d_print_info *dpi; - const struct demangle_component *dc; -{ - if (dc == NULL) - { - d_print_error (dpi); - return; - } - if (d_print_saw_error (dpi)) - return; - - switch (dc->type) - { - case DEMANGLE_COMPONENT_NAME: - if ((dpi->options & DMGL_JAVA) == 0) - d_append_buffer (dpi, dc->u.s_name.s, dc->u.s_name.len); - else - d_print_java_identifier (dpi, dc->u.s_name.s, dc->u.s_name.len); - return; - - case DEMANGLE_COMPONENT_QUAL_NAME: - case DEMANGLE_COMPONENT_LOCAL_NAME: - d_print_comp (dpi, d_left (dc)); - if ((dpi->options & DMGL_JAVA) == 0) - d_append_string_constant (dpi, "::"); - else - d_append_char (dpi, '.'); - d_print_comp (dpi, d_right (dc)); - return; - - case DEMANGLE_COMPONENT_TYPED_NAME: - { - struct d_print_mod *hold_modifiers; - struct demangle_component *typed_name; - struct d_print_mod adpm[4]; - unsigned int i; - struct d_print_template dpt; - - /* Pass the name down to the type so that it can be printed in - the right place for the type. We also have to pass down - any CV-qualifiers, which apply to the this parameter. */ - hold_modifiers = dpi->modifiers; - i = 0; - typed_name = d_left (dc); - while (typed_name != NULL) - { - if (i >= sizeof adpm / sizeof adpm[0]) - { - d_print_error (dpi); - return; - } - - adpm[i].next = dpi->modifiers; - dpi->modifiers = &adpm[i]; - adpm[i].mod = typed_name; - adpm[i].printed = 0; - adpm[i].templates = dpi->templates; - ++i; - - if (typed_name->type != DEMANGLE_COMPONENT_RESTRICT_THIS - && typed_name->type != DEMANGLE_COMPONENT_VOLATILE_THIS - && typed_name->type != DEMANGLE_COMPONENT_CONST_THIS) - break; - - typed_name = d_left (typed_name); - } - - /* If typed_name is a template, then it applies to the - function type as well. */ - if (typed_name->type == DEMANGLE_COMPONENT_TEMPLATE) - { - dpt.next = dpi->templates; - dpi->templates = &dpt; - dpt.template = typed_name; - } - - /* If typed_name is a DEMANGLE_COMPONENT_LOCAL_NAME, then - there may be CV-qualifiers on its right argument which - really apply here; this happens when parsing a class which - is local to a function. */ - if (typed_name->type == DEMANGLE_COMPONENT_LOCAL_NAME) - { - struct demangle_component *local_name; - - local_name = d_right (typed_name); - while (local_name->type == DEMANGLE_COMPONENT_RESTRICT_THIS - || local_name->type == DEMANGLE_COMPONENT_VOLATILE_THIS - || local_name->type == DEMANGLE_COMPONENT_CONST_THIS) - { - if (i >= sizeof adpm / sizeof adpm[0]) - { - d_print_error (dpi); - return; - } - - adpm[i] = adpm[i - 1]; - adpm[i].next = &adpm[i - 1]; - dpi->modifiers = &adpm[i]; - - adpm[i - 1].mod = local_name; - adpm[i - 1].printed = 0; - adpm[i - 1].templates = dpi->templates; - ++i; - - local_name = d_left (local_name); - } - } - - d_print_comp (dpi, d_right (dc)); - - if (typed_name->type == DEMANGLE_COMPONENT_TEMPLATE) - dpi->templates = dpt.next; - - /* If the modifiers didn't get printed by the type, print them - now. */ - while (i > 0) - { - --i; - if (! adpm[i].printed) - { - d_append_char (dpi, ' '); - d_print_mod (dpi, adpm[i].mod); - } - } - - dpi->modifiers = hold_modifiers; - - return; - } - - case DEMANGLE_COMPONENT_TEMPLATE: - { - struct d_print_mod *hold_dpm; - - /* Don't push modifiers into a template definition. Doing so - could give the wrong definition for a template argument. - Instead, treat the template essentially as a name. */ - - hold_dpm = dpi->modifiers; - dpi->modifiers = NULL; - - d_print_comp (dpi, d_left (dc)); - if (d_last_char (dpi) == '<') - d_append_char (dpi, ' '); - d_append_char (dpi, '<'); - d_print_comp (dpi, d_right (dc)); - /* Avoid generating two consecutive '>' characters, to avoid - the C++ syntactic ambiguity. */ - if (d_last_char (dpi) == '>') - d_append_char (dpi, ' '); - d_append_char (dpi, '>'); - - dpi->modifiers = hold_dpm; - - return; - } - - case DEMANGLE_COMPONENT_TEMPLATE_PARAM: - { - long i; - struct demangle_component *a; - struct d_print_template *hold_dpt; - - if (dpi->templates == NULL) - { - d_print_error (dpi); - return; - } - i = dc->u.s_number.number; - for (a = d_right (dpi->templates->template); - a != NULL; - a = d_right (a)) - { - if (a->type != DEMANGLE_COMPONENT_TEMPLATE_ARGLIST) - { - d_print_error (dpi); - return; - } - if (i <= 0) - break; - --i; - } - if (i != 0 || a == NULL) - { - d_print_error (dpi); - return; - } - - /* While processing this parameter, we need to pop the list of - templates. This is because the template parameter may - itself be a reference to a parameter of an outer - template. */ - - hold_dpt = dpi->templates; - dpi->templates = hold_dpt->next; - - d_print_comp (dpi, d_left (a)); - - dpi->templates = hold_dpt; - - return; - } - - case DEMANGLE_COMPONENT_CTOR: - d_print_comp (dpi, dc->u.s_ctor.name); - return; - - case DEMANGLE_COMPONENT_DTOR: - d_append_char (dpi, '~'); - d_print_comp (dpi, dc->u.s_dtor.name); - return; - - case DEMANGLE_COMPONENT_VTABLE: - d_append_string_constant (dpi, "vtable for "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_VTT: - d_append_string_constant (dpi, "VTT for "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE: - d_append_string_constant (dpi, "construction vtable for "); - d_print_comp (dpi, d_left (dc)); - d_append_string_constant (dpi, "-in-"); - d_print_comp (dpi, d_right (dc)); - return; - - case DEMANGLE_COMPONENT_TYPEINFO: - d_append_string_constant (dpi, "typeinfo for "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_TYPEINFO_NAME: - d_append_string_constant (dpi, "typeinfo name for "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_TYPEINFO_FN: - d_append_string_constant (dpi, "typeinfo fn for "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_THUNK: - d_append_string_constant (dpi, "non-virtual thunk to "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_VIRTUAL_THUNK: - d_append_string_constant (dpi, "virtual thunk to "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_COVARIANT_THUNK: - d_append_string_constant (dpi, "covariant return thunk to "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_JAVA_CLASS: - d_append_string_constant (dpi, "java Class for "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_GUARD: - d_append_string_constant (dpi, "guard variable for "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_REFTEMP: - d_append_string_constant (dpi, "reference temporary for "); - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_SUB_STD: - d_append_buffer (dpi, dc->u.s_string.string, dc->u.s_string.len); - return; - - case DEMANGLE_COMPONENT_RESTRICT: - case DEMANGLE_COMPONENT_VOLATILE: - case DEMANGLE_COMPONENT_CONST: - { - struct d_print_mod *pdpm; - - /* When printing arrays, it's possible to have cases where the - same CV-qualifier gets pushed on the stack multiple times. - We only need to print it once. */ - - for (pdpm = dpi->modifiers; pdpm != NULL; pdpm = pdpm->next) - { - if (! pdpm->printed) - { - if (pdpm->mod->type != DEMANGLE_COMPONENT_RESTRICT - && pdpm->mod->type != DEMANGLE_COMPONENT_VOLATILE - && pdpm->mod->type != DEMANGLE_COMPONENT_CONST) - break; - if (pdpm->mod->type == dc->type) - { - d_print_comp (dpi, d_left (dc)); - return; - } - } - } - } - /* Fall through. */ - case DEMANGLE_COMPONENT_RESTRICT_THIS: - case DEMANGLE_COMPONENT_VOLATILE_THIS: - case DEMANGLE_COMPONENT_CONST_THIS: - case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: - case DEMANGLE_COMPONENT_POINTER: - case DEMANGLE_COMPONENT_REFERENCE: - case DEMANGLE_COMPONENT_COMPLEX: - case DEMANGLE_COMPONENT_IMAGINARY: - { - /* We keep a list of modifiers on the stack. */ - struct d_print_mod dpm; - - dpm.next = dpi->modifiers; - dpi->modifiers = &dpm; - dpm.mod = dc; - dpm.printed = 0; - dpm.templates = dpi->templates; - - d_print_comp (dpi, d_left (dc)); - - /* If the modifier didn't get printed by the type, print it - now. */ - if (! dpm.printed) - d_print_mod (dpi, dc); - - dpi->modifiers = dpm.next; - - return; - } - - case DEMANGLE_COMPONENT_BUILTIN_TYPE: - if ((dpi->options & DMGL_JAVA) == 0) - d_append_buffer (dpi, dc->u.s_builtin.type->name, - dc->u.s_builtin.type->len); - else - d_append_buffer (dpi, dc->u.s_builtin.type->java_name, - dc->u.s_builtin.type->java_len); - return; - - case DEMANGLE_COMPONENT_VENDOR_TYPE: - d_print_comp (dpi, d_left (dc)); - return; - - case DEMANGLE_COMPONENT_FUNCTION_TYPE: - { - if (d_left (dc) != NULL) - { - struct d_print_mod dpm; - - /* We must pass this type down as a modifier in order to - print it in the right location. */ - - dpm.next = dpi->modifiers; - dpi->modifiers = &dpm; - dpm.mod = dc; - dpm.printed = 0; - dpm.templates = dpi->templates; - - d_print_comp (dpi, d_left (dc)); - - dpi->modifiers = dpm.next; - - if (dpm.printed) - return; - - d_append_char (dpi, ' '); - } - - d_print_function_type (dpi, dc, dpi->modifiers); - - return; - } - - case DEMANGLE_COMPONENT_ARRAY_TYPE: - { - struct d_print_mod *hold_modifiers; - struct d_print_mod adpm[4]; - unsigned int i; - struct d_print_mod *pdpm; - - /* We must pass this type down as a modifier in order to print - multi-dimensional arrays correctly. If the array itself is - CV-qualified, we act as though the element type were - CV-qualified. We do this by copying the modifiers down - rather than fiddling pointers, so that we don't wind up - with a d_print_mod higher on the stack pointing into our - stack frame after we return. */ - - hold_modifiers = dpi->modifiers; - - adpm[0].next = hold_modifiers; - dpi->modifiers = &adpm[0]; - adpm[0].mod = dc; - adpm[0].printed = 0; - adpm[0].templates = dpi->templates; - - i = 1; - pdpm = hold_modifiers; - while (pdpm != NULL - && (pdpm->mod->type == DEMANGLE_COMPONENT_RESTRICT - || pdpm->mod->type == DEMANGLE_COMPONENT_VOLATILE - || pdpm->mod->type == DEMANGLE_COMPONENT_CONST)) - { - if (! pdpm->printed) - { - if (i >= sizeof adpm / sizeof adpm[0]) - { - d_print_error (dpi); - return; - } - - adpm[i] = *pdpm; - adpm[i].next = dpi->modifiers; - dpi->modifiers = &adpm[i]; - pdpm->printed = 1; - ++i; - } - - pdpm = pdpm->next; - } - - d_print_comp (dpi, d_right (dc)); - - dpi->modifiers = hold_modifiers; - - if (adpm[0].printed) - return; - - while (i > 1) - { - --i; - d_print_mod (dpi, adpm[i].mod); - } - - d_print_array_type (dpi, dc, dpi->modifiers); - - return; - } - - case DEMANGLE_COMPONENT_PTRMEM_TYPE: - { - struct d_print_mod dpm; - - dpm.next = dpi->modifiers; - dpi->modifiers = &dpm; - dpm.mod = dc; - dpm.printed = 0; - dpm.templates = dpi->templates; - - d_print_comp (dpi, d_right (dc)); - - /* If the modifier didn't get printed by the type, print it - now. */ - if (! dpm.printed) - { - d_append_char (dpi, ' '); - d_print_comp (dpi, d_left (dc)); - d_append_string_constant (dpi, "::*"); - } - - dpi->modifiers = dpm.next; - - return; - } - - case DEMANGLE_COMPONENT_ARGLIST: - case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST: - d_print_comp (dpi, d_left (dc)); - if (d_right (dc) != NULL) - { - d_append_string_constant (dpi, ", "); - d_print_comp (dpi, d_right (dc)); - } - return; - - case DEMANGLE_COMPONENT_OPERATOR: - { - char c; - - d_append_string_constant (dpi, "operator"); - c = dc->u.s_operator.op->name[0]; - if (IS_LOWER (c)) - d_append_char (dpi, ' '); - d_append_buffer (dpi, dc->u.s_operator.op->name, - dc->u.s_operator.op->len); - return; - } - - case DEMANGLE_COMPONENT_EXTENDED_OPERATOR: - d_append_string_constant (dpi, "operator "); - d_print_comp (dpi, dc->u.s_extended_operator.name); - return; - - case DEMANGLE_COMPONENT_CAST: - d_append_string_constant (dpi, "operator "); - d_print_cast (dpi, dc); - return; - - case DEMANGLE_COMPONENT_UNARY: - if (d_left (dc)->type != DEMANGLE_COMPONENT_CAST) - d_print_expr_op (dpi, d_left (dc)); - else - { - d_append_char (dpi, '('); - d_print_cast (dpi, d_left (dc)); - d_append_char (dpi, ')'); - } - d_append_char (dpi, '('); - d_print_comp (dpi, d_right (dc)); - d_append_char (dpi, ')'); - return; - - case DEMANGLE_COMPONENT_BINARY: - if (d_right (dc)->type != DEMANGLE_COMPONENT_BINARY_ARGS) - { - d_print_error (dpi); - return; - } - - /* We wrap an expression which uses the greater-than operator in - an extra layer of parens so that it does not get confused - with the '>' which ends the template parameters. */ - if (d_left (dc)->type == DEMANGLE_COMPONENT_OPERATOR - && d_left (dc)->u.s_operator.op->len == 1 - && d_left (dc)->u.s_operator.op->name[0] == '>') - d_append_char (dpi, '('); - - d_append_char (dpi, '('); - d_print_comp (dpi, d_left (d_right (dc))); - d_append_string_constant (dpi, ") "); - d_print_expr_op (dpi, d_left (dc)); - d_append_string_constant (dpi, " ("); - d_print_comp (dpi, d_right (d_right (dc))); - d_append_char (dpi, ')'); - - if (d_left (dc)->type == DEMANGLE_COMPONENT_OPERATOR - && d_left (dc)->u.s_operator.op->len == 1 - && d_left (dc)->u.s_operator.op->name[0] == '>') - d_append_char (dpi, ')'); - - return; - - case DEMANGLE_COMPONENT_BINARY_ARGS: - /* We should only see this as part of DEMANGLE_COMPONENT_BINARY. */ - d_print_error (dpi); - return; - - case DEMANGLE_COMPONENT_TRINARY: - if (d_right (dc)->type != DEMANGLE_COMPONENT_TRINARY_ARG1 - || d_right (d_right (dc))->type != DEMANGLE_COMPONENT_TRINARY_ARG2) - { - d_print_error (dpi); - return; - } - d_append_char (dpi, '('); - d_print_comp (dpi, d_left (d_right (dc))); - d_append_string_constant (dpi, ") "); - d_print_expr_op (dpi, d_left (dc)); - d_append_string_constant (dpi, " ("); - d_print_comp (dpi, d_left (d_right (d_right (dc)))); - d_append_string_constant (dpi, ") : ("); - d_print_comp (dpi, d_right (d_right (d_right (dc)))); - d_append_char (dpi, ')'); - return; - - case DEMANGLE_COMPONENT_TRINARY_ARG1: - case DEMANGLE_COMPONENT_TRINARY_ARG2: - /* We should only see these are part of DEMANGLE_COMPONENT_TRINARY. */ - d_print_error (dpi); - return; - - case DEMANGLE_COMPONENT_LITERAL: - case DEMANGLE_COMPONENT_LITERAL_NEG: - { - enum d_builtin_type_print tp; - - /* For some builtin types, produce simpler output. */ - tp = D_PRINT_DEFAULT; - if (d_left (dc)->type == DEMANGLE_COMPONENT_BUILTIN_TYPE) - { - tp = d_left (dc)->u.s_builtin.type->print; - switch (tp) - { - case D_PRINT_INT: - case D_PRINT_UNSIGNED: - case D_PRINT_LONG: - case D_PRINT_UNSIGNED_LONG: - case D_PRINT_LONG_LONG: - case D_PRINT_UNSIGNED_LONG_LONG: - if (d_right (dc)->type == DEMANGLE_COMPONENT_NAME) - { - if (dc->type == DEMANGLE_COMPONENT_LITERAL_NEG) - d_append_char (dpi, '-'); - d_print_comp (dpi, d_right (dc)); - switch (tp) - { - default: - break; - case D_PRINT_UNSIGNED: - d_append_char (dpi, 'u'); - break; - case D_PRINT_LONG: - d_append_char (dpi, 'l'); - break; - case D_PRINT_UNSIGNED_LONG: - d_append_string_constant (dpi, "ul"); - break; - case D_PRINT_LONG_LONG: - d_append_string_constant (dpi, "ll"); - break; - case D_PRINT_UNSIGNED_LONG_LONG: - d_append_string_constant (dpi, "ull"); - break; - } - return; - } - break; - - case D_PRINT_BOOL: - if (d_right (dc)->type == DEMANGLE_COMPONENT_NAME - && d_right (dc)->u.s_name.len == 1 - && dc->type == DEMANGLE_COMPONENT_LITERAL) - { - switch (d_right (dc)->u.s_name.s[0]) - { - case '0': - d_append_string_constant (dpi, "false"); - return; - case '1': - d_append_string_constant (dpi, "true"); - return; - default: - break; - } - } - break; - - default: - break; - } - } - - d_append_char (dpi, '('); - d_print_comp (dpi, d_left (dc)); - d_append_char (dpi, ')'); - if (dc->type == DEMANGLE_COMPONENT_LITERAL_NEG) - d_append_char (dpi, '-'); - if (tp == D_PRINT_FLOAT) - d_append_char (dpi, '['); - d_print_comp (dpi, d_right (dc)); - if (tp == D_PRINT_FLOAT) - d_append_char (dpi, ']'); - } - return; - - default: - d_print_error (dpi); - return; - } -} - -/* Print a Java dentifier. For Java we try to handle encoded extended - Unicode characters. The C++ ABI doesn't mention Unicode encoding, - so we don't it for C++. Characters are encoded as - __U+_. */ - -static void -d_print_java_identifier (dpi, name, len) - struct d_print_info *dpi; - const char *name; - int len; -{ - const char *p; - const char *end; - - end = name + len; - for (p = name; p < end; ++p) - { - if (end - p > 3 - && p[0] == '_' - && p[1] == '_' - && p[2] == 'U') - { - unsigned long c; - const char *q; - - c = 0; - for (q = p + 3; q < end; ++q) - { - int dig; - - if (IS_DIGIT (*q)) - dig = *q - '0'; - else if (*q >= 'A' && *q <= 'F') - dig = *q - 'A' + 10; - else if (*q >= 'a' && *q <= 'f') - dig = *q - 'a' + 10; - else - break; - - c = c * 16 + dig; - } - /* If the Unicode character is larger than 256, we don't try - to deal with it here. FIXME. */ - if (q < end && *q == '_' && c < 256) - { - d_append_char (dpi, c); - p = q; - continue; - } - } - - d_append_char (dpi, *p); - } -} - -/* Print a list of modifiers. SUFFIX is 1 if we are printing - qualifiers on this after printing a function. */ - -static void -d_print_mod_list (dpi, mods, suffix) - struct d_print_info *dpi; - struct d_print_mod *mods; - int suffix; -{ - struct d_print_template *hold_dpt; - - if (mods == NULL || d_print_saw_error (dpi)) - return; - - if (mods->printed - || (! suffix - && (mods->mod->type == DEMANGLE_COMPONENT_RESTRICT_THIS - || mods->mod->type == DEMANGLE_COMPONENT_VOLATILE_THIS - || mods->mod->type == DEMANGLE_COMPONENT_CONST_THIS))) - { - d_print_mod_list (dpi, mods->next, suffix); - return; - } - - mods->printed = 1; - - hold_dpt = dpi->templates; - dpi->templates = mods->templates; - - if (mods->mod->type == DEMANGLE_COMPONENT_FUNCTION_TYPE) - { - d_print_function_type (dpi, mods->mod, mods->next); - dpi->templates = hold_dpt; - return; - } - else if (mods->mod->type == DEMANGLE_COMPONENT_ARRAY_TYPE) - { - d_print_array_type (dpi, mods->mod, mods->next); - dpi->templates = hold_dpt; - return; - } - else if (mods->mod->type == DEMANGLE_COMPONENT_LOCAL_NAME) - { - struct d_print_mod *hold_modifiers; - struct demangle_component *dc; - - /* When this is on the modifier stack, we have pulled any - qualifiers off the right argument already. Otherwise, we - print it as usual, but don't let the left argument see any - modifiers. */ - - hold_modifiers = dpi->modifiers; - dpi->modifiers = NULL; - d_print_comp (dpi, d_left (mods->mod)); - dpi->modifiers = hold_modifiers; - - if ((dpi->options & DMGL_JAVA) == 0) - d_append_string_constant (dpi, "::"); - else - d_append_char (dpi, '.'); - - dc = d_right (mods->mod); - while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS - || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS - || dc->type == DEMANGLE_COMPONENT_CONST_THIS) - dc = d_left (dc); - - d_print_comp (dpi, dc); - - dpi->templates = hold_dpt; - return; - } - - d_print_mod (dpi, mods->mod); - - dpi->templates = hold_dpt; - - d_print_mod_list (dpi, mods->next, suffix); -} - -/* Print a modifier. */ - -static void -d_print_mod (dpi, mod) - struct d_print_info *dpi; - const struct demangle_component *mod; -{ - switch (mod->type) - { - case DEMANGLE_COMPONENT_RESTRICT: - case DEMANGLE_COMPONENT_RESTRICT_THIS: - d_append_string_constant (dpi, " restrict"); - return; - case DEMANGLE_COMPONENT_VOLATILE: - case DEMANGLE_COMPONENT_VOLATILE_THIS: - d_append_string_constant (dpi, " volatile"); - return; - case DEMANGLE_COMPONENT_CONST: - case DEMANGLE_COMPONENT_CONST_THIS: - d_append_string_constant (dpi, " const"); - return; - case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: - d_append_char (dpi, ' '); - d_print_comp (dpi, d_right (mod)); - return; - case DEMANGLE_COMPONENT_POINTER: - /* There is no pointer symbol in Java. */ - if ((dpi->options & DMGL_JAVA) == 0) - d_append_char (dpi, '*'); - return; - case DEMANGLE_COMPONENT_REFERENCE: - d_append_char (dpi, '&'); - return; - case DEMANGLE_COMPONENT_COMPLEX: - d_append_string_constant (dpi, "complex "); - return; - case DEMANGLE_COMPONENT_IMAGINARY: - d_append_string_constant (dpi, "imaginary "); - return; - case DEMANGLE_COMPONENT_PTRMEM_TYPE: - if (d_last_char (dpi) != '(') - d_append_char (dpi, ' '); - d_print_comp (dpi, d_left (mod)); - d_append_string_constant (dpi, "::*"); - return; - case DEMANGLE_COMPONENT_TYPED_NAME: - d_print_comp (dpi, d_left (mod)); - return; - default: - /* Otherwise, we have something that won't go back on the - modifier stack, so we can just print it. */ - d_print_comp (dpi, mod); - return; - } -} - -/* Print a function type, except for the return type. */ - -static void -d_print_function_type (dpi, dc, mods) - struct d_print_info *dpi; - const struct demangle_component *dc; - struct d_print_mod *mods; -{ - int need_paren; - int saw_mod; - int need_space; - struct d_print_mod *p; - struct d_print_mod *hold_modifiers; - - need_paren = 0; - saw_mod = 0; - need_space = 0; - for (p = mods; p != NULL; p = p->next) - { - if (p->printed) - break; - - saw_mod = 1; - switch (p->mod->type) - { - case DEMANGLE_COMPONENT_POINTER: - case DEMANGLE_COMPONENT_REFERENCE: - need_paren = 1; - break; - case DEMANGLE_COMPONENT_RESTRICT: - case DEMANGLE_COMPONENT_VOLATILE: - case DEMANGLE_COMPONENT_CONST: - case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: - case DEMANGLE_COMPONENT_COMPLEX: - case DEMANGLE_COMPONENT_IMAGINARY: - case DEMANGLE_COMPONENT_PTRMEM_TYPE: - need_space = 1; - need_paren = 1; - break; - case DEMANGLE_COMPONENT_RESTRICT_THIS: - case DEMANGLE_COMPONENT_VOLATILE_THIS: - case DEMANGLE_COMPONENT_CONST_THIS: - break; - default: - break; - } - if (need_paren) - break; - } - - if (d_left (dc) != NULL && ! saw_mod) - need_paren = 1; - - if (need_paren) - { - if (! need_space) - { - if (d_last_char (dpi) != '(' - && d_last_char (dpi) != '*') - need_space = 1; - } - if (need_space && d_last_char (dpi) != ' ') - d_append_char (dpi, ' '); - d_append_char (dpi, '('); - } - - hold_modifiers = dpi->modifiers; - dpi->modifiers = NULL; - - d_print_mod_list (dpi, mods, 0); - - if (need_paren) - d_append_char (dpi, ')'); - - d_append_char (dpi, '('); - - if (d_right (dc) != NULL) - d_print_comp (dpi, d_right (dc)); - - d_append_char (dpi, ')'); - - d_print_mod_list (dpi, mods, 1); - - dpi->modifiers = hold_modifiers; -} - -/* Print an array type, except for the element type. */ - -static void -d_print_array_type (dpi, dc, mods) - struct d_print_info *dpi; - const struct demangle_component *dc; - struct d_print_mod *mods; -{ - int need_space; - - need_space = 1; - if (mods != NULL) - { - int need_paren; - struct d_print_mod *p; - - need_paren = 0; - for (p = mods; p != NULL; p = p->next) - { - if (! p->printed) - { - if (p->mod->type == DEMANGLE_COMPONENT_ARRAY_TYPE) - { - need_space = 0; - break; - } - else - { - need_paren = 1; - need_space = 1; - break; - } - } - } - - if (need_paren) - d_append_string_constant (dpi, " ("); - - d_print_mod_list (dpi, mods, 0); - - if (need_paren) - d_append_char (dpi, ')'); - } - - if (need_space) - d_append_char (dpi, ' '); - - d_append_char (dpi, '['); - - if (d_left (dc) != NULL) - d_print_comp (dpi, d_left (dc)); - - d_append_char (dpi, ']'); -} - -/* Print an operator in an expression. */ - -static void -d_print_expr_op (dpi, dc) - struct d_print_info *dpi; - const struct demangle_component *dc; -{ - if (dc->type == DEMANGLE_COMPONENT_OPERATOR) - d_append_buffer (dpi, dc->u.s_operator.op->name, - dc->u.s_operator.op->len); - else - d_print_comp (dpi, dc); -} - -/* Print a cast. */ - -static void -d_print_cast (dpi, dc) - struct d_print_info *dpi; - const struct demangle_component *dc; -{ - if (d_left (dc)->type != DEMANGLE_COMPONENT_TEMPLATE) - d_print_comp (dpi, d_left (dc)); - else - { - struct d_print_mod *hold_dpm; - struct d_print_template dpt; - - /* It appears that for a templated cast operator, we need to put - the template parameters in scope for the operator name, but - not for the parameters. The effect is that we need to handle - the template printing here. */ - - hold_dpm = dpi->modifiers; - dpi->modifiers = NULL; - - dpt.next = dpi->templates; - dpi->templates = &dpt; - dpt.template = d_left (dc); - - d_print_comp (dpi, d_left (d_left (dc))); - - dpi->templates = dpt.next; - - if (d_last_char (dpi) == '<') - d_append_char (dpi, ' '); - d_append_char (dpi, '<'); - d_print_comp (dpi, d_right (d_left (dc))); - /* Avoid generating two consecutive '>' characters, to avoid - the C++ syntactic ambiguity. */ - if (d_last_char (dpi) == '>') - d_append_char (dpi, ' '); - d_append_char (dpi, '>'); - - dpi->modifiers = hold_dpm; - } -} - -/* Initialize the information structure we use to pass around - information. */ - -CP_STATIC_IF_GLIBCPP_V3 -void -cplus_demangle_init_info (mangled, options, len, di) - const char *mangled; - int options; - size_t len; - struct d_info *di; -{ - di->s = mangled; - di->send = mangled + len; - di->options = options; - - di->n = mangled; - - /* We can not need more components than twice the number of chars in - the mangled string. Most components correspond directly to - chars, but the ARGLIST types are exceptions. */ - di->num_comps = 2 * len; - di->next_comp = 0; - - /* Similarly, we can not need more substitutions than there are - chars in the mangled string. */ - di->num_subs = len; - di->next_sub = 0; - di->did_subs = 0; - - di->last_name = NULL; - - di->expansion = 0; -} - -/* Entry point for the demangler. If MANGLED is a g++ v3 ABI mangled - name, return a buffer allocated with malloc holding the demangled - name. OPTIONS is the usual libiberty demangler options. On - success, this sets *PALC to the allocated size of the returned - buffer. On failure, this sets *PALC to 0 for a bad name, or 1 for - a memory allocation failure. On failure, this returns NULL. */ - -static char * -d_demangle (mangled, options, palc) - const char* mangled; - int options; - size_t *palc; -{ - size_t len; - int type; - struct d_info di; - struct demangle_component *dc; - int estimate; - char *ret; - - *palc = 0; - - len = strlen (mangled); - - if (mangled[0] == '_' && mangled[1] == 'Z') - type = 0; - else if (strncmp (mangled, "_GLOBAL_", 8) == 0 - && (mangled[8] == '.' || mangled[8] == '_' || mangled[8] == '$') - && (mangled[9] == 'D' || mangled[9] == 'I') - && mangled[10] == '_') - { - char *r; - - r = malloc (40 + len - 11); - if (r == NULL) - *palc = 1; - else - { - if (mangled[9] == 'I') - strcpy (r, "global constructors keyed to "); - else - strcpy (r, "global destructors keyed to "); - strcat (r, mangled + 11); - } - return r; - } - else - { - if ((options & DMGL_TYPES) == 0) - return NULL; - type = 1; - } - - cplus_demangle_init_info (mangled, options, len, &di); - - { -#ifdef CP_DYNAMIC_ARRAYS - __extension__ struct demangle_component comps[di.num_comps]; - __extension__ struct demangle_component *subs[di.num_subs]; - - di.comps = &comps[0]; - di.subs = &subs[0]; -#else - di.comps = ((struct demangle_component *) - malloc (di.num_comps * sizeof (struct demangle_component))); - di.subs = ((struct demangle_component **) - malloc (di.num_subs * sizeof (struct demangle_component *))); - if (di.comps == NULL || di.subs == NULL) - { - if (di.comps != NULL) - free (di.comps); - if (di.subs != NULL) - free (di.subs); - *palc = 1; - return NULL; - } -#endif - - if (! type) - dc = cplus_demangle_mangled_name (&di, 1); - else - dc = cplus_demangle_type (&di); - - /* If DMGL_PARAMS is set, then if we didn't consume the entire - mangled string, then we didn't successfully demangle it. If - DMGL_PARAMS is not set, we didn't look at the trailing - parameters. */ - if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0') - dc = NULL; - -#ifdef CP_DEMANGLE_DEBUG - if (dc == NULL) - printf ("failed demangling\n"); - else - d_dump (dc, 0); -#endif - - /* We try to guess the length of the demangled string, to minimize - calls to realloc during demangling. */ - estimate = len + di.expansion + 10 * di.did_subs; - estimate += estimate / 8; - - ret = NULL; - if (dc != NULL) - ret = cplus_demangle_print (options, dc, estimate, palc); - -#ifndef CP_DYNAMIC_ARRAYS - free (di.comps); - free (di.subs); -#endif - -#ifdef CP_DEMANGLE_DEBUG - if (ret != NULL) - { - int rlen; - - rlen = strlen (ret); - if (rlen > 2 * estimate) - printf ("*** Length %d much greater than estimate %d\n", - rlen, estimate); - else if (rlen > estimate) - printf ("*** Length %d greater than estimate %d\n", - rlen, estimate); - else if (rlen < estimate / 2) - printf ("*** Length %d much less than estimate %d\n", - rlen, estimate); - } -#endif - } - - return ret; -} - -static char * -cplus_demangle_v3 (mangled, options) - const char* mangled; - int options; -{ - size_t alc; - - return d_demangle (mangled, options, &alc); -} - -/* Demangle a Java symbol. Java uses a subset of the V3 ABI C++ mangling - conventions, but the output formatting is a little different. - This instructs the C++ demangler not to emit pointer characters ("*"), and - to use Java's namespace separator symbol ("." instead of "::"). It then - does an additional pass over the demangled output to replace instances - of JArray with TYPE[]. */ - -static char * -java_demangle_v3 (mangled) - const char* mangled; -{ - size_t alc; - char *demangled; - int nesting; - char *from; - char *to; - - demangled = d_demangle (mangled, DMGL_JAVA | DMGL_PARAMS, &alc); - - if (demangled == NULL) - return NULL; - - nesting = 0; - from = demangled; - to = from; - while (*from != '\0') - { - if (strncmp (from, "JArray<", 7) == 0) - { - from += 7; - ++nesting; - } - else if (nesting > 0 && *from == '>') - { - while (to > demangled && to[-1] == ' ') - --to; - *to++ = '['; - *to++ = ']'; - --nesting; - ++from; - } - else - *to++ = *from++; - } - - *to = '\0'; - - return demangled; -} - -#ifndef IN_GLIBCPP_V3 - -/* Demangle a string in order to find out whether it is a constructor - or destructor. Return non-zero on success. Set *CTOR_KIND and - *DTOR_KIND appropriately. */ - -static int -is_ctor_or_dtor (mangled, ctor_kind, dtor_kind) - const char *mangled; - enum gnu_v3_ctor_kinds *ctor_kind; - enum gnu_v3_dtor_kinds *dtor_kind; -{ - struct d_info di; - struct demangle_component *dc; - int ret; - - *ctor_kind = (enum gnu_v3_ctor_kinds) 0; - *dtor_kind = (enum gnu_v3_dtor_kinds) 0; - - cplus_demangle_init_info (mangled, DMGL_GNU_V3, strlen (mangled), &di); - - { -#ifdef CP_DYNAMIC_ARRAYS - __extension__ struct demangle_component comps[di.num_comps]; - __extension__ struct demangle_component *subs[di.num_subs]; - - di.comps = &comps[0]; - di.subs = &subs[0]; -#else - di.comps = ((struct demangle_component *) - malloc (di.num_comps * sizeof (struct demangle_component))); - di.subs = ((struct demangle_component **) - malloc (di.num_subs * sizeof (struct demangle_component *))); - if (di.comps == NULL || di.subs == NULL) - { - if (di.comps != NULL) - free (di.comps); - if (di.subs != NULL) - free (di.subs); - return 0; - } -#endif - - dc = cplus_demangle_mangled_name (&di, 1); - - /* Note that because we did not pass DMGL_PARAMS, we don't expect - to demangle the entire string. */ - - ret = 0; - while (dc != NULL) - { - switch (dc->type) - { - default: - dc = NULL; - break; - case DEMANGLE_COMPONENT_TYPED_NAME: - case DEMANGLE_COMPONENT_TEMPLATE: - case DEMANGLE_COMPONENT_RESTRICT_THIS: - case DEMANGLE_COMPONENT_VOLATILE_THIS: - case DEMANGLE_COMPONENT_CONST_THIS: - dc = d_left (dc); - break; - case DEMANGLE_COMPONENT_QUAL_NAME: - case DEMANGLE_COMPONENT_LOCAL_NAME: - dc = d_right (dc); - break; - case DEMANGLE_COMPONENT_CTOR: - *ctor_kind = dc->u.s_ctor.kind; - ret = 1; - dc = NULL; - break; - case DEMANGLE_COMPONENT_DTOR: - *dtor_kind = dc->u.s_dtor.kind; - ret = 1; - dc = NULL; - break; - } - } - -#ifndef CP_DYNAMIC_ARRAYS - free (di.subs); - free (di.comps); -#endif - } - - return ret; -} - -/* Return whether NAME is the mangled form of a g++ V3 ABI constructor - name. A non-zero return indicates the type of constructor. */ - -enum gnu_v3_ctor_kinds -is_gnu_v3_mangled_ctor (name) - const char *name; -{ - enum gnu_v3_ctor_kinds ctor_kind; - enum gnu_v3_dtor_kinds dtor_kind; - - if (! is_ctor_or_dtor (name, &ctor_kind, &dtor_kind)) - return (enum gnu_v3_ctor_kinds) 0; - return ctor_kind; -} - - -/* Return whether NAME is the mangled form of a g++ V3 ABI destructor - name. A non-zero return indicates the type of destructor. */ - -enum gnu_v3_dtor_kinds -is_gnu_v3_mangled_dtor (name) - const char *name; -{ - enum gnu_v3_ctor_kinds ctor_kind; - enum gnu_v3_dtor_kinds dtor_kind; - - if (! is_ctor_or_dtor (name, &ctor_kind, &dtor_kind)) - return (enum gnu_v3_dtor_kinds) 0; - return dtor_kind; -} - -#endif /* IN_GLIBCPP_V3 */ -/* Demangler for GNU C++ - Copyright 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. - Written by James Clark (jjc@jclark.uucp) - Rewritten by Fred Fish (fnf@cygnus.com) for ARM and Lucid demangling - Modified by Satish Pai (pai@apollo.hp.com) for HP demangling - -This file is part of the libiberty library. -Libiberty is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -In addition to the permissions in the GNU Library General Public -License, the Free Software Foundation gives you unlimited permission -to link the compiled version of this file into combinations with other -programs, and to distribute those combinations without any restriction -coming from the use of this file. (The Library Public License -restrictions do apply in other respects; for example, they cover -modification of the file, and distribution when not linked into a -combined executable.) - -Libiberty 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with libiberty; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -/* This file exports two functions; cplus_mangle_opname and cplus_demangle. - - This file imports xmalloc and g_realloc, which are like malloc and - realloc except that they generate a fatal error if there is no - available memory. */ - -/* This file lives in both GCC and libiberty. When making changes, please - try not to break either. */ - -#undef CURRENT_DEMANGLING_STYLE -#define CURRENT_DEMANGLING_STYLE work->options - -static char *ada_demangle PARAMS ((const char *, int)); - -#define min(X,Y) (((X) < (Y)) ? (X) : (Y)) - -/* A value at least one greater than the maximum number of characters - that will be output when using the `%d' format with `printf'. */ -#define INTBUF_SIZE 32 - -extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN; - -/* In order to allow a single demangler executable to demangle strings - using various common values of CPLUS_MARKER, as well as any specific - one set at compile time, we maintain a string containing all the - commonly used ones, and check to see if the marker we are looking for - is in that string. CPLUS_MARKER is usually '$' on systems where the - assembler can deal with that. Where the assembler can't, it's usually - '.' (but on many systems '.' is used for other things). We put the - current defined CPLUS_MARKER first (which defaults to '$'), followed - by the next most common value, followed by an explicit '$' in case - the value of CPLUS_MARKER is not '$'. - - We could avoid this if we could just get g++ to tell us what the actual - cplus marker character is as part of the debug information, perhaps by - ensuring that it is the character that terminates the gcc_compiled - marker symbol (FIXME). */ - -#if !defined (CPLUS_MARKER) -#define CPLUS_MARKER '$' -#endif - -static enum demangling_styles current_demangling_style = auto_demangling; - -static char cplus_markers[] = { CPLUS_MARKER, '.', '$', '\0' }; - -static char char_str[2] = { '\000', '\000' }; - -typedef struct string /* Beware: these aren't required to be */ -{ /* '\0' terminated. */ - char *b; /* pointer to start of string */ - char *p; /* pointer after last character */ - char *e; /* pointer after end of allocated space */ -} string; - -/* Stuff that is shared between sub-routines. - Using a shared structure allows cplus_demangle to be reentrant. */ - -struct work_stuff -{ - int options; - char **typevec; - char **ktypevec; - char **btypevec; - int numk; - int numb; - int ksize; - int bsize; - int ntypes; - int typevec_size; - int constructor; - int destructor; - int static_type; /* A static member function */ - int temp_start; /* index in demangled to start of template args */ - int type_quals; /* The type qualifiers. */ - int dllimported; /* Symbol imported from a PE DLL */ - char **tmpl_argvec; /* Template function arguments. */ - int ntmpl_args; /* The number of template function arguments. */ - int forgetting_types; /* Nonzero if we are not remembering the types - we see. */ - string* previous_argument; /* The last function argument demangled. */ - int nrepeats; /* The number of times to repeat the previous - argument. */ -}; - -#define PRINT_ANSI_QUALIFIERS (work -> options & DMGL_ANSI) -#define PRINT_ARG_TYPES (work -> options & DMGL_PARAMS) - -static const struct optable -{ - const char *const in; - const char *const out; - const int flags; -} optable[] = { - {"nw", " new", DMGL_ANSI}, /* new (1.92, ansi) */ - {"dl", " delete", DMGL_ANSI}, /* new (1.92, ansi) */ - {"new", " new", 0}, /* old (1.91, and 1.x) */ - {"delete", " delete", 0}, /* old (1.91, and 1.x) */ - {"vn", " new []", DMGL_ANSI}, /* GNU, pending ansi */ - {"vd", " delete []", DMGL_ANSI}, /* GNU, pending ansi */ - {"as", "=", DMGL_ANSI}, /* ansi */ - {"ne", "!=", DMGL_ANSI}, /* old, ansi */ - {"eq", "==", DMGL_ANSI}, /* old, ansi */ - {"ge", ">=", DMGL_ANSI}, /* old, ansi */ - {"gt", ">", DMGL_ANSI}, /* old, ansi */ - {"le", "<=", DMGL_ANSI}, /* old, ansi */ - {"lt", "<", DMGL_ANSI}, /* old, ansi */ - {"plus", "+", 0}, /* old */ - {"pl", "+", DMGL_ANSI}, /* ansi */ - {"apl", "+=", DMGL_ANSI}, /* ansi */ - {"minus", "-", 0}, /* old */ - {"mi", "-", DMGL_ANSI}, /* ansi */ - {"ami", "-=", DMGL_ANSI}, /* ansi */ - {"mult", "*", 0}, /* old */ - {"ml", "*", DMGL_ANSI}, /* ansi */ - {"amu", "*=", DMGL_ANSI}, /* ansi (ARM/Lucid) */ - {"aml", "*=", DMGL_ANSI}, /* ansi (GNU/g++) */ - {"convert", "+", 0}, /* old (unary +) */ - {"negate", "-", 0}, /* old (unary -) */ - {"trunc_mod", "%", 0}, /* old */ - {"md", "%", DMGL_ANSI}, /* ansi */ - {"amd", "%=", DMGL_ANSI}, /* ansi */ - {"trunc_div", "/", 0}, /* old */ - {"dv", "/", DMGL_ANSI}, /* ansi */ - {"adv", "/=", DMGL_ANSI}, /* ansi */ - {"truth_andif", "&&", 0}, /* old */ - {"aa", "&&", DMGL_ANSI}, /* ansi */ - {"truth_orif", "||", 0}, /* old */ - {"oo", "||", DMGL_ANSI}, /* ansi */ - {"truth_not", "!", 0}, /* old */ - {"nt", "!", DMGL_ANSI}, /* ansi */ - {"postincrement","++", 0}, /* old */ - {"pp", "++", DMGL_ANSI}, /* ansi */ - {"postdecrement","--", 0}, /* old */ - {"mm", "--", DMGL_ANSI}, /* ansi */ - {"bit_ior", "|", 0}, /* old */ - {"or", "|", DMGL_ANSI}, /* ansi */ - {"aor", "|=", DMGL_ANSI}, /* ansi */ - {"bit_xor", "^", 0}, /* old */ - {"er", "^", DMGL_ANSI}, /* ansi */ - {"aer", "^=", DMGL_ANSI}, /* ansi */ - {"bit_and", "&", 0}, /* old */ - {"ad", "&", DMGL_ANSI}, /* ansi */ - {"aad", "&=", DMGL_ANSI}, /* ansi */ - {"bit_not", "~", 0}, /* old */ - {"co", "~", DMGL_ANSI}, /* ansi */ - {"call", "()", 0}, /* old */ - {"cl", "()", DMGL_ANSI}, /* ansi */ - {"alshift", "<<", 0}, /* old */ - {"ls", "<<", DMGL_ANSI}, /* ansi */ - {"als", "<<=", DMGL_ANSI}, /* ansi */ - {"arshift", ">>", 0}, /* old */ - {"rs", ">>", DMGL_ANSI}, /* ansi */ - {"ars", ">>=", DMGL_ANSI}, /* ansi */ - {"component", "->", 0}, /* old */ - {"pt", "->", DMGL_ANSI}, /* ansi; Lucid C++ form */ - {"rf", "->", DMGL_ANSI}, /* ansi; ARM/GNU form */ - {"indirect", "*", 0}, /* old */ - {"method_call", "->()", 0}, /* old */ - {"addr", "&", 0}, /* old (unary &) */ - {"array", "[]", 0}, /* old */ - {"vc", "[]", DMGL_ANSI}, /* ansi */ - {"compound", ", ", 0}, /* old */ - {"cm", ", ", DMGL_ANSI}, /* ansi */ - {"cond", "?:", 0}, /* old */ - {"cn", "?:", DMGL_ANSI}, /* pseudo-ansi */ - {"max", ">?", 0}, /* old */ - {"mx", ">?", DMGL_ANSI}, /* pseudo-ansi */ - {"min", "*", DMGL_ANSI}, /* ansi */ - {"sz", "sizeof ", DMGL_ANSI} /* pseudo-ansi */ -}; - -/* These values are used to indicate the various type varieties. - They are all non-zero so that they can be used as `success' - values. */ -typedef enum type_kind_t -{ - tk_none, - tk_pointer, - tk_reference, - tk_integral, - tk_bool, - tk_char, - tk_real -} type_kind_t; - -static const struct demangler_engine libiberty_demanglers[] = -{ - { - NO_DEMANGLING_STYLE_STRING, - no_demangling, - "Demangling disabled" - } - , - { - AUTO_DEMANGLING_STYLE_STRING, - auto_demangling, - "Automatic selection based on executable" - } - , - { - GNU_DEMANGLING_STYLE_STRING, - gnu_demangling, - "GNU (g++) style demangling" - } - , - { - LUCID_DEMANGLING_STYLE_STRING, - lucid_demangling, - "Lucid (lcc) style demangling" - } - , - { - ARM_DEMANGLING_STYLE_STRING, - arm_demangling, - "ARM style demangling" - } - , - { - HP_DEMANGLING_STYLE_STRING, - hp_demangling, - "HP (aCC) style demangling" - } - , - { - EDG_DEMANGLING_STYLE_STRING, - edg_demangling, - "EDG style demangling" - } - , - { - GNU_V3_DEMANGLING_STYLE_STRING, - gnu_v3_demangling, - "GNU (g++) V3 ABI-style demangling" - } - , - { - JAVA_DEMANGLING_STYLE_STRING, - java_demangling, - "Java style demangling" - } - , - { - GNAT_DEMANGLING_STYLE_STRING, - gnat_demangling, - "GNAT style demangling" - } - , - { - NULL, unknown_demangling, NULL - } -}; - -#define STRING_EMPTY(str) ((str) -> b == (str) -> p) -#define APPEND_BLANK(str) {if (!STRING_EMPTY(str)) \ - string_append(str, " ");} -#define LEN_STRING(str) ( (STRING_EMPTY(str))?0:((str)->p - (str)->b)) - -/* The scope separator appropriate for the language being demangled. */ - -#define SCOPE_STRING(work) ((work->options & DMGL_JAVA) ? "." : "::") - -#define ARM_VTABLE_STRING "__vtbl__" /* Lucid/ARM virtual table prefix */ -#define ARM_VTABLE_STRLEN 8 /* strlen (ARM_VTABLE_STRING) */ - -/* Prototypes for local functions */ - -static void -delete_work_stuff PARAMS ((struct work_stuff *)); - -static void -delete_non_B_K_work_stuff PARAMS ((struct work_stuff *)); - -static char * -mop_up PARAMS ((struct work_stuff *, string *, int)); - -static void -squangle_mop_up PARAMS ((struct work_stuff *)); - -static void -work_stuff_copy_to_from PARAMS ((struct work_stuff *, struct work_stuff *)); - -#if 0 -static int -demangle_method_args PARAMS ((struct work_stuff *, const char **, string *)); -#endif - -static char * -internal_cplus_demangle PARAMS ((struct work_stuff *, const char *)); - -static int -demangle_template_template_parm PARAMS ((struct work_stuff *work, - const char **, string *)); - -static int -demangle_template PARAMS ((struct work_stuff *work, const char **, string *, - string *, int, int)); - -static int -arm_pt PARAMS ((struct work_stuff *, const char *, int, const char **, - const char **)); - -static int -demangle_class_name PARAMS ((struct work_stuff *, const char **, string *)); - -static int -demangle_qualified PARAMS ((struct work_stuff *, const char **, string *, - int, int)); - -static int -demangle_class PARAMS ((struct work_stuff *, const char **, string *)); - -static int -demangle_fund_type PARAMS ((struct work_stuff *, const char **, string *)); - -static int -demangle_signature PARAMS ((struct work_stuff *, const char **, string *)); - -static int -demangle_prefix PARAMS ((struct work_stuff *, const char **, string *)); - -static int -gnu_special PARAMS ((struct work_stuff *, const char **, string *)); - -static int -arm_special PARAMS ((const char **, string *)); - -static void -string_need PARAMS ((string *, int)); - -static void -string_delete PARAMS ((string *)); - -static void -string_init PARAMS ((string *)); - -static void -string_clear PARAMS ((string *)); - -#if 0 -static int -string_empty PARAMS ((string *)); -#endif - -static void -string_append PARAMS ((string *, const char *)); - -static void -string_appends PARAMS ((string *, string *)); - -static void -string_appendn PARAMS ((string *, const char *, int)); - -static void -string_prepend PARAMS ((string *, const char *)); - -static void -string_prependn PARAMS ((string *, const char *, int)); - -static void -string_append_template_idx PARAMS ((string *, int)); - -static int -get_count PARAMS ((const char **, int *)); - -static int -consume_count PARAMS ((const char **)); - -static int -consume_count_with_underscores PARAMS ((const char**)); - -static int -demangle_args PARAMS ((struct work_stuff *, const char **, string *)); - -static int -demangle_nested_args PARAMS ((struct work_stuff*, const char**, string*)); - -static int -do_type PARAMS ((struct work_stuff *, const char **, string *)); - -static int -do_arg PARAMS ((struct work_stuff *, const char **, string *)); - -static void -demangle_function_name PARAMS ((struct work_stuff *, const char **, string *, - const char *)); - -static int -iterate_demangle_function PARAMS ((struct work_stuff *, - const char **, string *, const char *)); - -static void -remember_type PARAMS ((struct work_stuff *, const char *, int)); - -static void -remember_Btype PARAMS ((struct work_stuff *, const char *, int, int)); - -static int -register_Btype PARAMS ((struct work_stuff *)); - -static void -remember_Ktype PARAMS ((struct work_stuff *, const char *, int)); - -static void -forget_types PARAMS ((struct work_stuff *)); - -static void -forget_B_and_K_types PARAMS ((struct work_stuff *)); - -static void -string_prepends PARAMS ((string *, string *)); - -static int -demangle_template_value_parm PARAMS ((struct work_stuff*, const char**, - string*, type_kind_t)); - -static int -do_hpacc_template_const_value PARAMS ((struct work_stuff *, const char **, string *)); - -static int -do_hpacc_template_literal PARAMS ((struct work_stuff *, const char **, string *)); - -static int -snarf_numeric_literal PARAMS ((const char **, string *)); - -/* There is a TYPE_QUAL value for each type qualifier. They can be - combined by bitwise-or to form the complete set of qualifiers for a - type. */ - -#define TYPE_UNQUALIFIED 0x0 -#define TYPE_QUAL_CONST 0x1 -#define TYPE_QUAL_VOLATILE 0x2 -#define TYPE_QUAL_RESTRICT 0x4 - -static int -code_for_qualifier PARAMS ((int)); - -static const char* -qualifier_string PARAMS ((int)); - -static const char* -demangle_qualifier PARAMS ((int)); - -static int -demangle_expression PARAMS ((struct work_stuff *, const char **, string *, - type_kind_t)); - -static int -demangle_integral_value PARAMS ((struct work_stuff *, const char **, - string *)); - -static int -demangle_real_value PARAMS ((struct work_stuff *, const char **, string *)); - -static void -demangle_arm_hp_template PARAMS ((struct work_stuff *, const char **, int, - string *)); - -static void -recursively_demangle PARAMS ((struct work_stuff *, const char **, string *, - int)); - -static void -grow_vect PARAMS ((char **, size_t *, size_t, int)); - -/* Translate count to integer, consuming tokens in the process. - Conversion terminates on the first non-digit character. - - Trying to consume something that isn't a count results in no - consumption of input and a return of -1. - - Overflow consumes the rest of the digits, and returns -1. */ - -static int -consume_count (type) - const char **type; -{ - int count = 0; - - if (! g_ascii_isdigit ((unsigned char)**type)) - return -1; - - while (g_ascii_isdigit ((unsigned char)**type)) - { - count *= 10; - - /* Check for overflow. - We assume that count is represented using two's-complement; - no power of two is divisible by ten, so if an overflow occurs - when multiplying by ten, the result will not be a multiple of - ten. */ - if ((count % 10) != 0) - { - while (g_ascii_isdigit ((unsigned char) **type)) - (*type)++; - return -1; - } - - count += **type - '0'; - (*type)++; - } - - if (count < 0) - count = -1; - - return (count); -} - - -/* Like consume_count, but for counts that are preceded and followed - by '_' if they are greater than 10. Also, -1 is returned for - failure, since 0 can be a valid value. */ - -static int -consume_count_with_underscores (mangled) - const char **mangled; -{ - int idx; - - if (**mangled == '_') - { - (*mangled)++; - if (!g_ascii_isdigit ((unsigned char)**mangled)) - return -1; - - idx = consume_count (mangled); - if (**mangled != '_') - /* The trailing underscore was missing. */ - return -1; - - (*mangled)++; - } - else - { - if (**mangled < '0' || **mangled > '9') - return -1; - - idx = **mangled - '0'; - (*mangled)++; - } - - return idx; -} - -/* C is the code for a type-qualifier. Return the TYPE_QUAL - corresponding to this qualifier. */ - -static int -code_for_qualifier (c) - int c; -{ - switch (c) - { - case 'C': - return TYPE_QUAL_CONST; - - case 'V': - return TYPE_QUAL_VOLATILE; - - case 'u': - return TYPE_QUAL_RESTRICT; - - default: - break; - } - - /* C was an invalid qualifier. */ - abort (); -} - -/* Return the string corresponding to the qualifiers given by - TYPE_QUALS. */ - -static const char* -qualifier_string (type_quals) - int type_quals; -{ - switch (type_quals) - { - case TYPE_UNQUALIFIED: - return ""; - - case TYPE_QUAL_CONST: - return "const"; - - case TYPE_QUAL_VOLATILE: - return "volatile"; - - case TYPE_QUAL_RESTRICT: - return "__restrict"; - - case TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE: - return "const volatile"; - - case TYPE_QUAL_CONST | TYPE_QUAL_RESTRICT: - return "const __restrict"; - - case TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT: - return "volatile __restrict"; - - case TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT: - return "const volatile __restrict"; - - default: - break; - } - - /* TYPE_QUALS was an invalid qualifier set. */ - abort (); -} - -/* C is the code for a type-qualifier. Return the string - corresponding to this qualifier. This function should only be - called with a valid qualifier code. */ - -static const char* -demangle_qualifier (c) - int c; -{ - return qualifier_string (code_for_qualifier (c)); -} - -/* Takes operator name as e.g. "++" and returns mangled - operator name (e.g. "postincrement_expr"), or NULL if not found. - - If OPTIONS & DMGL_ANSI == 1, return the ANSI name; - if OPTIONS & DMGL_ANSI == 0, return the old GNU name. */ - -/* Add a routine to set the demangling style to be sure it is valid and - allow for any demangler initialization that maybe necessary. */ - -/* Do string name to style translation */ - -/* char *cplus_demangle (const char *mangled, int options) - - If MANGLED is a mangled function name produced by GNU C++, then - a pointer to a @code{malloc}ed string giving a C++ representation - of the name will be returned; otherwise NULL will be returned. - It is the caller's responsibility to free the string which - is returned. - - The OPTIONS arg may contain one or more of the following bits: - - DMGL_ANSI ANSI qualifiers such as `const' and `void' are - included. - DMGL_PARAMS Function parameters are included. - - For example, - - cplus_demangle ("foo__1Ai", DMGL_PARAMS) => "A::foo(int)" - cplus_demangle ("foo__1Ai", DMGL_PARAMS | DMGL_ANSI) => "A::foo(int)" - cplus_demangle ("foo__1Ai", 0) => "A::foo" - - cplus_demangle ("foo__1Afe", DMGL_PARAMS) => "A::foo(float,...)" - cplus_demangle ("foo__1Afe", DMGL_PARAMS | DMGL_ANSI)=> "A::foo(float,...)" - cplus_demangle ("foo__1Afe", 0) => "A::foo" - - Note that any leading underscores, or other such characters prepended by - the compilation system, are presumed to have already been stripped from - MANGLED. */ - -char * -sysprof_cplus_demangle (mangled, options) - const char *mangled; - int options; -{ - char *ret; - struct work_stuff work[1]; - - if (current_demangling_style == no_demangling) - return g_strdup (mangled); - - memset ((char *) work, 0, sizeof (work)); - work->options = options; - if ((work->options & DMGL_STYLE_MASK) == 0) - work->options |= (int) current_demangling_style & DMGL_STYLE_MASK; - - /* The V3 ABI demangling is implemented elsewhere. */ - if (GNU_V3_DEMANGLING || AUTO_DEMANGLING) - { - ret = cplus_demangle_v3 (mangled, work->options); - if (ret || GNU_V3_DEMANGLING) - return ret; - } - - if (JAVA_DEMANGLING) - { - ret = java_demangle_v3 (mangled); - if (ret) - return ret; - } - - if (GNAT_DEMANGLING) - return ada_demangle(mangled,options); - - ret = internal_cplus_demangle (work, mangled); - squangle_mop_up (work); - return (ret); -} - - -/* Assuming *OLD_VECT points to an array of *SIZE objects of size - ELEMENT_SIZE, grow it to contain at least MIN_SIZE objects, - updating *OLD_VECT and *SIZE as necessary. */ - -static void -grow_vect (old_vect, size, min_size, element_size) - char **old_vect; - size_t *size; - size_t min_size; - int element_size; -{ - if (*size < min_size) - { - *size *= 2; - if (*size < min_size) - *size = min_size; - *old_vect = (void *) g_realloc (*old_vect, *size * element_size); - } -} - -/* Demangle ada names: - 1. Discard final __{DIGIT}+ or ${DIGIT}+ - 2. Convert other instances of embedded "__" to `.'. - 3. Discard leading _ada_. - 4. Remove everything after first ___ if it is followed by 'X'. - 5. Put symbols that should be suppressed in <...> brackets. - The resulting string is valid until the next call of ada_demangle. */ - -static char * -ada_demangle (mangled, option) - const char *mangled; - int option ATTRIBUTE_UNUSED; -{ - int i, j; - int len0; - const char* p; - char *demangled = NULL; - int changed; - size_t demangled_size = 0; - - changed = 0; - - if (strncmp (mangled, "_ada_", 5) == 0) - { - mangled += 5; - changed = 1; - } - - if (mangled[0] == '_' || mangled[0] == '<') - goto Suppress; - - p = strstr (mangled, "___"); - if (p == NULL) - len0 = strlen (mangled); - else - { - if (p[3] == 'X') - { - len0 = p - mangled; - changed = 1; - } - else - goto Suppress; - } - - /* Make demangled big enough for possible expansion by operator name. */ - grow_vect (&demangled, - &demangled_size, 2 * len0 + 1, - sizeof (char)); - - if (g_ascii_isdigit ((unsigned char) mangled[len0 - 1])) { - for (i = len0 - 2; i >= 0 && g_ascii_isdigit ((unsigned char) mangled[i]); i -= 1) - ; - if (i > 1 && mangled[i] == '_' && mangled[i - 1] == '_') - { - len0 = i - 1; - changed = 1; - } - else if (mangled[i] == '$') - { - len0 = i; - changed = 1; - } - } - - for (i = 0, j = 0; i < len0 && ! g_ascii_isalpha ((unsigned char)mangled[i]); - i += 1, j += 1) - demangled[j] = mangled[i]; - - while (i < len0) - { - if (i < len0 - 2 && mangled[i] == '_' && mangled[i + 1] == '_') - { - demangled[j] = '.'; - changed = 1; - i += 2; j += 1; - } - else - { - demangled[j] = mangled[i]; - i += 1; j += 1; - } - } - demangled[j] = '\000'; - - for (i = 0; demangled[i] != '\0'; i += 1) - if (g_ascii_isupper ((unsigned char)demangled[i]) || demangled[i] == ' ') - goto Suppress; - - if (! changed) - return NULL; - else - return demangled; - - Suppress: - grow_vect (&demangled, - &demangled_size, strlen (mangled) + 3, - sizeof (char)); - - if (mangled[0] == '<') - strcpy (demangled, mangled); - else - sprintf (demangled, "<%s>", mangled); - - return demangled; -} - -/* This function performs most of what cplus_demangle use to do, but - to be able to demangle a name with a B, K or n code, we need to - have a longer term memory of what types have been seen. The original - now initializes and cleans up the squangle code info, while internal - calls go directly to this routine to avoid resetting that info. */ - -static char * -internal_cplus_demangle (work, mangled) - struct work_stuff *work; - const char *mangled; -{ - - string decl; - int success = 0; - char *demangled = NULL; - int s1, s2, s3, s4; - s1 = work->constructor; - s2 = work->destructor; - s3 = work->static_type; - s4 = work->type_quals; - work->constructor = work->destructor = 0; - work->type_quals = TYPE_UNQUALIFIED; - work->dllimported = 0; - - if ((mangled != NULL) && (*mangled != '\0')) - { - string_init (&decl); - - /* First check to see if gnu style demangling is active and if the - string to be demangled contains a CPLUS_MARKER. If so, attempt to - recognize one of the gnu special forms rather than looking for a - standard prefix. In particular, don't worry about whether there - is a "__" string in the mangled string. Consider "_$_5__foo" for - example. */ - - if ((AUTO_DEMANGLING || GNU_DEMANGLING)) - { - success = gnu_special (work, &mangled, &decl); - } - if (!success) - { - success = demangle_prefix (work, &mangled, &decl); - } - if (success && (*mangled != '\0')) - { - success = demangle_signature (work, &mangled, &decl); - } - if (work->constructor == 2) - { - string_prepend (&decl, "global constructors keyed to "); - work->constructor = 0; - } - else if (work->destructor == 2) - { - string_prepend (&decl, "global destructors keyed to "); - work->destructor = 0; - } - else if (work->dllimported == 1) - { - string_prepend (&decl, "import stub for "); - work->dllimported = 0; - } - demangled = mop_up (work, &decl, success); - } - work->constructor = s1; - work->destructor = s2; - work->static_type = s3; - work->type_quals = s4; - return demangled; -} - - -/* Clear out and squangling related storage */ -static void -squangle_mop_up (work) - struct work_stuff *work; -{ - /* clean up the B and K type mangling types. */ - forget_B_and_K_types (work); - if (work -> btypevec != NULL) - { - g_free ((char *) work -> btypevec); - } - if (work -> ktypevec != NULL) - { - g_free ((char *) work -> ktypevec); - } -} - - -/* Copy the work state and storage. */ - -static void -work_stuff_copy_to_from (to, from) - struct work_stuff *to; - struct work_stuff *from; -{ - int i; - - delete_work_stuff (to); - - /* Shallow-copy scalars. */ - memcpy (to, from, sizeof (*to)); - - /* Deep-copy dynamic storage. */ - if (from->typevec_size) - to->typevec - = (char **) g_malloc (from->typevec_size * sizeof (to->typevec[0])); - - for (i = 0; i < from->ntypes; i++) - { - int len = strlen (from->typevec[i]) + 1; - - to->typevec[i] = g_malloc (len); - memcpy (to->typevec[i], from->typevec[i], len); - } - - if (from->ksize) - to->ktypevec - = (char **) g_malloc (from->ksize * sizeof (to->ktypevec[0])); - - for (i = 0; i < from->numk; i++) - { - int len = strlen (from->ktypevec[i]) + 1; - - to->ktypevec[i] = g_malloc (len); - memcpy (to->ktypevec[i], from->ktypevec[i], len); - } - - if (from->bsize) - to->btypevec - = (char **) g_malloc (from->bsize * sizeof (to->btypevec[0])); - - for (i = 0; i < from->numb; i++) - { - int len = strlen (from->btypevec[i]) + 1; - - to->btypevec[i] = g_malloc (len); - memcpy (to->btypevec[i], from->btypevec[i], len); - } - - if (from->ntmpl_args) - to->tmpl_argvec - = (char **) g_malloc (from->ntmpl_args * sizeof (to->tmpl_argvec[0])); - - for (i = 0; i < from->ntmpl_args; i++) - { - int len = strlen (from->tmpl_argvec[i]) + 1; - - to->tmpl_argvec[i] = g_malloc (len); - memcpy (to->tmpl_argvec[i], from->tmpl_argvec[i], len); - } - - if (from->previous_argument) - { - to->previous_argument = (string*) g_malloc (sizeof (string)); - string_init (to->previous_argument); - string_appends (to->previous_argument, from->previous_argument); - } -} - - -/* Delete dynamic stuff in work_stuff that is not to be re-used. */ - -static void -delete_non_B_K_work_stuff (work) - struct work_stuff *work; -{ - /* Discard the remembered types, if any. */ - - forget_types (work); - if (work -> typevec != NULL) - { - g_free ((char *) work -> typevec); - work -> typevec = NULL; - work -> typevec_size = 0; - } - if (work->tmpl_argvec) - { - int i; - - for (i = 0; i < work->ntmpl_args; i++) - if (work->tmpl_argvec[i]) - g_free ((char*) work->tmpl_argvec[i]); - - g_free ((char*) work->tmpl_argvec); - work->tmpl_argvec = NULL; - } - if (work->previous_argument) - { - string_delete (work->previous_argument); - g_free ((char*) work->previous_argument); - work->previous_argument = NULL; - } -} - - -/* Delete all dynamic storage in work_stuff. */ -static void -delete_work_stuff (work) - struct work_stuff *work; -{ - delete_non_B_K_work_stuff (work); - squangle_mop_up (work); -} - - -/* Clear out any mangled storage */ - -static char * -mop_up (work, declp, success) - struct work_stuff *work; - string *declp; - int success; -{ - char *demangled = NULL; - - delete_non_B_K_work_stuff (work); - - /* If demangling was successful, ensure that the demangled string is null - terminated and return it. Otherwise, free the demangling decl. */ - - if (!success) - { - string_delete (declp); - } - else - { - string_appendn (declp, "", 1); - demangled = declp->b; - } - return (demangled); -} - -/* - -LOCAL FUNCTION - - demangle_signature -- demangle the signature part of a mangled name - -SYNOPSIS - - static int - demangle_signature (struct work_stuff *work, const char **mangled, - string *declp); - -DESCRIPTION - - Consume and demangle the signature portion of the mangled name. - - DECLP is the string where demangled output is being built. At - entry it contains the demangled root name from the mangled name - prefix. I.E. either a demangled operator name or the root function - name. In some special cases, it may contain nothing. - - *MANGLED points to the current unconsumed location in the mangled - name. As tokens are consumed and demangling is performed, the - pointer is updated to continuously point at the next token to - be consumed. - - Demangling GNU style mangled names is nasty because there is no - explicit token that marks the start of the outermost function - argument list. */ - -static int -demangle_signature (work, mangled, declp) - struct work_stuff *work; - const char **mangled; - string *declp; -{ - int success = 1; - int func_done = 0; - int expect_func = 0; - int expect_return_type = 0; - const char *oldmangled = NULL; - string trawname; - string tname; - - while (success && (**mangled != '\0')) - { - switch (**mangled) - { - case 'Q': - oldmangled = *mangled; - success = demangle_qualified (work, mangled, declp, 1, 0); - if (success) - remember_type (work, oldmangled, *mangled - oldmangled); - if (AUTO_DEMANGLING || GNU_DEMANGLING) - expect_func = 1; - oldmangled = NULL; - break; - - case 'K': - oldmangled = *mangled; - success = demangle_qualified (work, mangled, declp, 1, 0); - if (AUTO_DEMANGLING || GNU_DEMANGLING) - { - expect_func = 1; - } - oldmangled = NULL; - break; - - case 'S': - /* Static member function */ - if (oldmangled == NULL) - { - oldmangled = *mangled; - } - (*mangled)++; - work -> static_type = 1; - break; - - case 'C': - case 'V': - case 'u': - work->type_quals |= code_for_qualifier (**mangled); - - /* a qualified member function */ - if (oldmangled == NULL) - oldmangled = *mangled; - (*mangled)++; - break; - - case 'L': - /* Local class name follows after "Lnnn_" */ - if (HP_DEMANGLING) - { - while (**mangled && (**mangled != '_')) - (*mangled)++; - if (!**mangled) - success = 0; - else - (*mangled)++; - } - else - success = 0; - break; - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - if (oldmangled == NULL) - { - oldmangled = *mangled; - } - work->temp_start = -1; /* uppermost call to demangle_class */ - success = demangle_class (work, mangled, declp); - if (success) - { - remember_type (work, oldmangled, *mangled - oldmangled); - } - if (AUTO_DEMANGLING || GNU_DEMANGLING || EDG_DEMANGLING) - { - /* EDG and others will have the "F", so we let the loop cycle - if we are looking at one. */ - if (**mangled != 'F') - expect_func = 1; - } - oldmangled = NULL; - break; - - case 'B': - { - string s; - success = do_type (work, mangled, &s); - if (success) - { - string_append (&s, SCOPE_STRING (work)); - string_prepends (declp, &s); - string_delete (&s); - } - oldmangled = NULL; - expect_func = 1; - } - break; - - case 'F': - /* Function */ - /* ARM/HP style demangling includes a specific 'F' character after - the class name. For GNU style, it is just implied. So we can - safely just consume any 'F' at this point and be compatible - with either style. */ - - oldmangled = NULL; - func_done = 1; - (*mangled)++; - - /* For lucid/ARM/HP style we have to forget any types we might - have remembered up to this point, since they were not argument - types. GNU style considers all types seen as available for - back references. See comment in demangle_args() */ - - if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) - { - forget_types (work); - } - success = demangle_args (work, mangled, declp); - /* After picking off the function args, we expect to either - find the function return type (preceded by an '_') or the - end of the string. */ - if (success && (AUTO_DEMANGLING || EDG_DEMANGLING) && **mangled == '_') - { - ++(*mangled); - /* At this level, we do not care about the return type. */ - success = do_type (work, mangled, &tname); - string_delete (&tname); - } - - break; - - case 't': - /* G++ Template */ - string_init(&trawname); - string_init(&tname); - if (oldmangled == NULL) - { - oldmangled = *mangled; - } - success = demangle_template (work, mangled, &tname, - &trawname, 1, 1); - if (success) - { - remember_type (work, oldmangled, *mangled - oldmangled); - } - string_append (&tname, SCOPE_STRING (work)); - - string_prepends(declp, &tname); - if (work -> destructor & 1) - { - string_prepend (&trawname, "~"); - string_appends (declp, &trawname); - work->destructor -= 1; - } - if ((work->constructor & 1) || (work->destructor & 1)) - { - string_appends (declp, &trawname); - work->constructor -= 1; - } - string_delete(&trawname); - string_delete(&tname); - oldmangled = NULL; - expect_func = 1; - break; - - case '_': - if ((AUTO_DEMANGLING || GNU_DEMANGLING) && expect_return_type) - { - /* Read the return type. */ - string return_type; - - (*mangled)++; - success = do_type (work, mangled, &return_type); - APPEND_BLANK (&return_type); - - string_prepends (declp, &return_type); - string_delete (&return_type); - break; - } - else - /* At the outermost level, we cannot have a return type specified, - so if we run into another '_' at this point we are dealing with - a mangled name that is either bogus, or has been mangled by - some algorithm we don't know how to deal with. So just - reject the entire demangling. */ - /* However, "_nnn" is an expected suffix for alternate entry point - numbered nnn for a function, with HP aCC, so skip over that - without reporting failure. pai/1997-09-04 */ - if (HP_DEMANGLING) - { - (*mangled)++; - while (**mangled && g_ascii_isdigit ((unsigned char)**mangled)) - (*mangled)++; - } - else - success = 0; - break; - - case 'H': - if (AUTO_DEMANGLING || GNU_DEMANGLING) - { - /* A G++ template function. Read the template arguments. */ - success = demangle_template (work, mangled, declp, 0, 0, - 0); - if (!(work->constructor & 1)) - expect_return_type = 1; - (*mangled)++; - break; - } - else - /* fall through */ - {;} - - default: - if (AUTO_DEMANGLING || GNU_DEMANGLING) - { - /* Assume we have stumbled onto the first outermost function - argument token, and start processing args. */ - func_done = 1; - success = demangle_args (work, mangled, declp); - } - else - { - /* Non-GNU demanglers use a specific token to mark the start - of the outermost function argument tokens. Typically 'F', - for ARM/HP-demangling, for example. So if we find something - we are not prepared for, it must be an error. */ - success = 0; - } - break; - } - /* - if (AUTO_DEMANGLING || GNU_DEMANGLING) - */ - { - if (success && expect_func) - { - func_done = 1; - if (LUCID_DEMANGLING || ARM_DEMANGLING || EDG_DEMANGLING) - { - forget_types (work); - } - success = demangle_args (work, mangled, declp); - /* Since template include the mangling of their return types, - we must set expect_func to 0 so that we don't try do - demangle more arguments the next time we get here. */ - expect_func = 0; - } - } - } - if (success && !func_done) - { - if (AUTO_DEMANGLING || GNU_DEMANGLING) - { - /* With GNU style demangling, bar__3foo is 'foo::bar(void)', and - bar__3fooi is 'foo::bar(int)'. We get here when we find the - first case, and need to ensure that the '(void)' gets added to - the current declp. Note that with ARM/HP, the first case - represents the name of a static data member 'foo::bar', - which is in the current declp, so we leave it alone. */ - success = demangle_args (work, mangled, declp); - } - } - if (success && PRINT_ARG_TYPES) - { - if (work->static_type) - string_append (declp, " static"); - if (work->type_quals != TYPE_UNQUALIFIED) - { - APPEND_BLANK (declp); - string_append (declp, qualifier_string (work->type_quals)); - } - } - - return (success); -} - -#if 0 - -static int -demangle_method_args (work, mangled, declp) - struct work_stuff *work; - const char **mangled; - string *declp; -{ - int success = 0; - - if (work -> static_type) - { - string_append (declp, *mangled + 1); - *mangled += strlen (*mangled); - success = 1; - } - else - { - success = demangle_args (work, mangled, declp); - } - return (success); -} - -#endif - -static int -demangle_template_template_parm (work, mangled, tname) - struct work_stuff *work; - const char **mangled; - string *tname; -{ - int i; - int r; - int need_comma = 0; - int success = 1; - string temp; - - string_append (tname, "template <"); - /* get size of template parameter list */ - if (get_count (mangled, &r)) - { - for (i = 0; i < r; i++) - { - if (need_comma) - { - string_append (tname, ", "); - } - - /* Z for type parameters */ - if (**mangled == 'Z') - { - (*mangled)++; - string_append (tname, "class"); - } - /* z for template parameters */ - else if (**mangled == 'z') - { - (*mangled)++; - success = - demangle_template_template_parm (work, mangled, tname); - if (!success) - { - break; - } - } - else - { - /* temp is initialized in do_type */ - success = do_type (work, mangled, &temp); - if (success) - { - string_appends (tname, &temp); - } - string_delete(&temp); - if (!success) - { - break; - } - } - need_comma = 1; - } - - } - if (tname->p[-1] == '>') - string_append (tname, " "); - string_append (tname, "> class"); - return (success); -} - -static int -demangle_expression (work, mangled, s, tk) - struct work_stuff *work; - const char** mangled; - string* s; - type_kind_t tk; -{ - int need_operator = 0; - int success; - - success = 1; - string_appendn (s, "(", 1); - (*mangled)++; - while (success && **mangled != 'W' && **mangled != '\0') - { - if (need_operator) - { - size_t i; - size_t len; - - success = 0; - - len = strlen (*mangled); - - for (i = 0; i < G_N_ELEMENTS (optable); ++i) - { - size_t l = strlen (optable[i].in); - - if (l <= len - && memcmp (optable[i].in, *mangled, l) == 0) - { - string_appendn (s, " ", 1); - string_append (s, optable[i].out); - string_appendn (s, " ", 1); - success = 1; - (*mangled) += l; - break; - } - } - - if (!success) - break; - } - else - need_operator = 1; - - success = demangle_template_value_parm (work, mangled, s, tk); - } - - if (**mangled != 'W') - success = 0; - else - { - string_appendn (s, ")", 1); - (*mangled)++; - } - - return success; -} - -static int -demangle_integral_value (work, mangled, s) - struct work_stuff *work; - const char** mangled; - string* s; -{ - int success; - - if (**mangled == 'E') - success = demangle_expression (work, mangled, s, tk_integral); - else if (**mangled == 'Q' || **mangled == 'K') - success = demangle_qualified (work, mangled, s, 0, 1); - else - { - int value; - - /* By default, we let the number decide whether we shall consume an - underscore. */ - int multidigit_without_leading_underscore = 0; - int leave_following_underscore = 0; - - success = 0; - - if (**mangled == '_') - { - if (mangled[0][1] == 'm') - { - /* Since consume_count_with_underscores does not handle the - `m'-prefix we must do it here, using consume_count and - adjusting underscores: we have to consume the underscore - matching the prepended one. */ - multidigit_without_leading_underscore = 1; - string_appendn (s, "-", 1); - (*mangled) += 2; - } - else - { - /* Do not consume a following underscore; - consume_count_with_underscores will consume what - should be consumed. */ - leave_following_underscore = 1; - } - } - else - { - /* Negative numbers are indicated with a leading `m'. */ - if (**mangled == 'm') - { - string_appendn (s, "-", 1); - (*mangled)++; - } - /* Since consume_count_with_underscores does not handle - multi-digit numbers that do not start with an underscore, - and this number can be an integer template parameter, - we have to call consume_count. */ - multidigit_without_leading_underscore = 1; - /* These multi-digit numbers never end on an underscore, - so if there is one then don't eat it. */ - leave_following_underscore = 1; - } - - /* We must call consume_count if we expect to remove a trailing - underscore, since consume_count_with_underscores expects - the leading underscore (that we consumed) if it is to handle - multi-digit numbers. */ - if (multidigit_without_leading_underscore) - value = consume_count (mangled); - else - value = consume_count_with_underscores (mangled); - - if (value != -1) - { - char buf[INTBUF_SIZE]; - sprintf (buf, "%d", value); - string_append (s, buf); - - /* Numbers not otherwise delimited, might have an underscore - appended as a delimeter, which we should skip. - - ??? This used to always remove a following underscore, which - is wrong. If other (arbitrary) cases are followed by an - underscore, we need to do something more radical. */ - - if ((value > 9 || multidigit_without_leading_underscore) - && ! leave_following_underscore - && **mangled == '_') - (*mangled)++; - - /* All is well. */ - success = 1; - } - } - - return success; -} - -/* Demangle the real value in MANGLED. */ - -static int -demangle_real_value (work, mangled, s) - struct work_stuff *work; - const char **mangled; - string* s; -{ - if (**mangled == 'E') - return demangle_expression (work, mangled, s, tk_real); - - if (**mangled == 'm') - { - string_appendn (s, "-", 1); - (*mangled)++; - } - while (g_ascii_isdigit ((unsigned char)**mangled)) - { - string_appendn (s, *mangled, 1); - (*mangled)++; - } - if (**mangled == '.') /* fraction */ - { - string_appendn (s, ".", 1); - (*mangled)++; - while (g_ascii_isdigit ((unsigned char)**mangled)) - { - string_appendn (s, *mangled, 1); - (*mangled)++; - } - } - if (**mangled == 'e') /* exponent */ - { - string_appendn (s, "e", 1); - (*mangled)++; - while (g_ascii_isdigit ((unsigned char)**mangled)) - { - string_appendn (s, *mangled, 1); - (*mangled)++; - } - } - - return 1; -} - -static int -demangle_template_value_parm (work, mangled, s, tk) - struct work_stuff *work; - const char **mangled; - string* s; - type_kind_t tk; -{ - int success = 1; - - if (**mangled == 'Y') - { - /* The next argument is a template parameter. */ - int idx; - - (*mangled)++; - idx = consume_count_with_underscores (mangled); - if (idx == -1 - || (work->tmpl_argvec && idx >= work->ntmpl_args) - || consume_count_with_underscores (mangled) == -1) - return -1; - if (work->tmpl_argvec) - string_append (s, work->tmpl_argvec[idx]); - else - string_append_template_idx (s, idx); - } - else if (tk == tk_integral) - success = demangle_integral_value (work, mangled, s); - else if (tk == tk_char) - { - char tmp[2]; - int val; - if (**mangled == 'm') - { - string_appendn (s, "-", 1); - (*mangled)++; - } - string_appendn (s, "'", 1); - val = consume_count(mangled); - if (val <= 0) - success = 0; - else - { - tmp[0] = (char)val; - tmp[1] = '\0'; - string_appendn (s, &tmp[0], 1); - string_appendn (s, "'", 1); - } - } - else if (tk == tk_bool) - { - int val = consume_count (mangled); - if (val == 0) - string_appendn (s, "false", 5); - else if (val == 1) - string_appendn (s, "true", 4); - else - success = 0; - } - else if (tk == tk_real) - success = demangle_real_value (work, mangled, s); - else if (tk == tk_pointer || tk == tk_reference) - { - if (**mangled == 'Q') - success = demangle_qualified (work, mangled, s, - /*isfuncname=*/0, - /*append=*/1); - else - { - int symbol_len = consume_count (mangled); - if (symbol_len == -1) - return -1; - if (symbol_len == 0) - string_appendn (s, "0", 1); - else - { - char *p = g_malloc (symbol_len + 1), *q; - strncpy (p, *mangled, symbol_len); - p [symbol_len] = '\0'; - /* We use cplus_demangle here, rather than - internal_cplus_demangle, because the name of the entity - mangled here does not make use of any of the squangling - or type-code information we have built up thus far; it is - mangled independently. */ - q = sysprof_cplus_demangle (p, work->options); - if (tk == tk_pointer) - string_appendn (s, "&", 1); - /* FIXME: Pointer-to-member constants should get a - qualifying class name here. */ - if (q) - { - string_append (s, q); - g_free (q); - } - else - string_append (s, p); - g_free (p); - } - *mangled += symbol_len; - } - } - - return success; -} - -/* Demangle the template name in MANGLED. The full name of the - template (e.g., S) is placed in TNAME. The name without the - template parameters (e.g. S) is placed in TRAWNAME if TRAWNAME is - non-NULL. If IS_TYPE is nonzero, this template is a type template, - not a function template. If both IS_TYPE and REMEMBER are nonzero, - the template is remembered in the list of back-referenceable - types. */ - -static int -demangle_template (work, mangled, tname, trawname, is_type, remember) - struct work_stuff *work; - const char **mangled; - string *tname; - string *trawname; - int is_type; - int remember; -{ - int i; - int r; - int need_comma = 0; - int success = 0; - int is_java_array = 0; - string temp; - - (*mangled)++; - if (is_type) - { - /* get template name */ - if (**mangled == 'z') - { - int idx; - (*mangled)++; - (*mangled)++; - - idx = consume_count_with_underscores (mangled); - if (idx == -1 - || (work->tmpl_argvec && idx >= work->ntmpl_args) - || consume_count_with_underscores (mangled) == -1) - return (0); - - if (work->tmpl_argvec) - { - string_append (tname, work->tmpl_argvec[idx]); - if (trawname) - string_append (trawname, work->tmpl_argvec[idx]); - } - else - { - string_append_template_idx (tname, idx); - if (trawname) - string_append_template_idx (trawname, idx); - } - } - else - { - if ((r = consume_count (mangled)) <= 0 - || (int) strlen (*mangled) < r) - { - return (0); - } - is_java_array = (work -> options & DMGL_JAVA) - && strncmp (*mangled, "JArray1Z", 8) == 0; - if (! is_java_array) - { - string_appendn (tname, *mangled, r); - } - if (trawname) - string_appendn (trawname, *mangled, r); - *mangled += r; - } - } - if (!is_java_array) - string_append (tname, "<"); - /* get size of template parameter list */ - if (!get_count (mangled, &r)) - { - return (0); - } - if (!is_type) - { - /* Create an array for saving the template argument values. */ - work->tmpl_argvec = (char**) g_malloc (r * sizeof (char *)); - work->ntmpl_args = r; - for (i = 0; i < r; i++) - work->tmpl_argvec[i] = 0; - } - for (i = 0; i < r; i++) - { - if (need_comma) - { - string_append (tname, ", "); - } - /* Z for type parameters */ - if (**mangled == 'Z') - { - (*mangled)++; - /* temp is initialized in do_type */ - success = do_type (work, mangled, &temp); - if (success) - { - string_appends (tname, &temp); - - if (!is_type) - { - /* Save the template argument. */ - int len = temp.p - temp.b; - work->tmpl_argvec[i] = g_malloc (len + 1); - memcpy (work->tmpl_argvec[i], temp.b, len); - work->tmpl_argvec[i][len] = '\0'; - } - } - string_delete(&temp); - if (!success) - { - break; - } - } - /* z for template parameters */ - else if (**mangled == 'z') - { - int r2; - (*mangled)++; - success = demangle_template_template_parm (work, mangled, tname); - - if (success - && (r2 = consume_count (mangled)) > 0 - && (int) strlen (*mangled) >= r2) - { - string_append (tname, " "); - string_appendn (tname, *mangled, r2); - if (!is_type) - { - /* Save the template argument. */ - int len = r2; - work->tmpl_argvec[i] = g_malloc (len + 1); - memcpy (work->tmpl_argvec[i], *mangled, len); - work->tmpl_argvec[i][len] = '\0'; - } - *mangled += r2; - } - if (!success) - { - break; - } - } - else - { - string param; - string* s; - - /* otherwise, value parameter */ - - /* temp is initialized in do_type */ - success = do_type (work, mangled, &temp); - string_delete(&temp); - if (!success) - break; - - if (!is_type) - { - s = ¶m; - string_init (s); - } - else - s = tname; - - success = demangle_template_value_parm (work, mangled, s, - (type_kind_t) success); - - if (!success) - { - if (!is_type) - string_delete (s); - success = 0; - break; - } - - if (!is_type) - { - int len = s->p - s->b; - work->tmpl_argvec[i] = g_malloc (len + 1); - memcpy (work->tmpl_argvec[i], s->b, len); - work->tmpl_argvec[i][len] = '\0'; - - string_appends (tname, s); - string_delete (s); - } - } - need_comma = 1; - } - if (is_java_array) - { - string_append (tname, "[]"); - } - else - { - if (tname->p[-1] == '>') - string_append (tname, " "); - string_append (tname, ">"); - } - - if (is_type && remember) - { - const int bindex = register_Btype (work); - remember_Btype (work, tname->b, LEN_STRING (tname), bindex); - } - - /* - if (work -> static_type) - { - string_append (declp, *mangled + 1); - *mangled += strlen (*mangled); - success = 1; - } - else - { - success = demangle_args (work, mangled, declp); - } - } - */ - return (success); -} - -static int -arm_pt (work, mangled, n, anchor, args) - struct work_stuff *work; - const char *mangled; - int n; - const char **anchor, **args; -{ - /* Check if ARM template with "__pt__" in it ("parameterized type") */ - /* Allow HP also here, because HP's cfront compiler follows ARM to some extent */ - if ((ARM_DEMANGLING || HP_DEMANGLING) && (*anchor = strstr (mangled, "__pt__"))) - { - int len; - *args = *anchor + 6; - len = consume_count (args); - if (len == -1) - return 0; - if (*args + len == mangled + n && **args == '_') - { - ++*args; - return 1; - } - } - if (AUTO_DEMANGLING || EDG_DEMANGLING) - { - if ((*anchor = strstr (mangled, "__tm__")) - || (*anchor = strstr (mangled, "__ps__")) - || (*anchor = strstr (mangled, "__pt__"))) - { - int len; - *args = *anchor + 6; - len = consume_count (args); - if (len == -1) - return 0; - if (*args + len == mangled + n && **args == '_') - { - ++*args; - return 1; - } - } - else if ((*anchor = strstr (mangled, "__S"))) - { - int len; - *args = *anchor + 3; - len = consume_count (args); - if (len == -1) - return 0; - if (*args + len == mangled + n && **args == '_') - { - ++*args; - return 1; - } - } - } - - return 0; -} - -static void -demangle_arm_hp_template (work, mangled, n, declp) - struct work_stuff *work; - const char **mangled; - int n; - string *declp; -{ - const char *p; - const char *args; - const char *e = *mangled + n; - string arg; - - /* Check for HP aCC template spec: classXt1t2 where t1, t2 are - template args */ - if (HP_DEMANGLING && ((*mangled)[n] == 'X')) - { - char *start_spec_args = NULL; - int hold_options; - - /* First check for and omit template specialization pseudo-arguments, - such as in "Spec<#1,#1.*>" */ - start_spec_args = strchr (*mangled, '<'); - if (start_spec_args && (start_spec_args - *mangled < n)) - string_appendn (declp, *mangled, start_spec_args - *mangled); - else - string_appendn (declp, *mangled, n); - (*mangled) += n + 1; - string_init (&arg); - if (work->temp_start == -1) /* non-recursive call */ - work->temp_start = declp->p - declp->b; - - /* We want to unconditionally demangle parameter types in - template parameters. */ - hold_options = work->options; - work->options |= DMGL_PARAMS; - - string_append (declp, "<"); - while (1) - { - string_delete (&arg); - switch (**mangled) - { - case 'T': - /* 'T' signals a type parameter */ - (*mangled)++; - if (!do_type (work, mangled, &arg)) - goto hpacc_template_args_done; - break; - - case 'U': - case 'S': - /* 'U' or 'S' signals an integral value */ - if (!do_hpacc_template_const_value (work, mangled, &arg)) - goto hpacc_template_args_done; - break; - - case 'A': - /* 'A' signals a named constant expression (literal) */ - if (!do_hpacc_template_literal (work, mangled, &arg)) - goto hpacc_template_args_done; - break; - - default: - /* Today, 1997-09-03, we have only the above types - of template parameters */ - /* FIXME: maybe this should fail and return null */ - goto hpacc_template_args_done; - } - string_appends (declp, &arg); - /* Check if we're at the end of template args. - 0 if at end of static member of template class, - _ if done with template args for a function */ - if ((**mangled == '\000') || (**mangled == '_')) - break; - else - string_append (declp, ","); - } - hpacc_template_args_done: - string_append (declp, ">"); - string_delete (&arg); - if (**mangled == '_') - (*mangled)++; - work->options = hold_options; - return; - } - /* ARM template? (Also handles HP cfront extensions) */ - else if (arm_pt (work, *mangled, n, &p, &args)) - { - int hold_options; - string type_str; - - string_init (&arg); - string_appendn (declp, *mangled, p - *mangled); - if (work->temp_start == -1) /* non-recursive call */ - work->temp_start = declp->p - declp->b; - - /* We want to unconditionally demangle parameter types in - template parameters. */ - hold_options = work->options; - work->options |= DMGL_PARAMS; - - string_append (declp, "<"); - /* should do error checking here */ - while (args < e) { - string_delete (&arg); - - /* Check for type or literal here */ - switch (*args) - { - /* HP cfront extensions to ARM for template args */ - /* spec: Xt1Lv1 where t1 is a type, v1 is a literal value */ - /* FIXME: We handle only numeric literals for HP cfront */ - case 'X': - /* A typed constant value follows */ - args++; - if (!do_type (work, &args, &type_str)) - goto cfront_template_args_done; - string_append (&arg, "("); - string_appends (&arg, &type_str); - string_delete (&type_str); - string_append (&arg, ")"); - if (*args != 'L') - goto cfront_template_args_done; - args++; - /* Now snarf a literal value following 'L' */ - if (!snarf_numeric_literal (&args, &arg)) - goto cfront_template_args_done; - break; - - case 'L': - /* Snarf a literal following 'L' */ - args++; - if (!snarf_numeric_literal (&args, &arg)) - goto cfront_template_args_done; - break; - default: - /* Not handling other HP cfront stuff */ - { - const char* old_args = args; - if (!do_type (work, &args, &arg)) - goto cfront_template_args_done; - - /* Fail if we didn't make any progress: prevent infinite loop. */ - if (args == old_args) - { - work->options = hold_options; - return; - } - } - } - string_appends (declp, &arg); - string_append (declp, ","); - } - cfront_template_args_done: - string_delete (&arg); - if (args >= e) - --declp->p; /* remove extra comma */ - string_append (declp, ">"); - work->options = hold_options; - } - else if (n>10 && strncmp (*mangled, "_GLOBAL_", 8) == 0 - && (*mangled)[9] == 'N' - && (*mangled)[8] == (*mangled)[10] - && strchr (cplus_markers, (*mangled)[8])) - { - /* A member of the anonymous namespace. */ - string_append (declp, "{anonymous}"); - } - else - { - if (work->temp_start == -1) /* non-recursive call only */ - work->temp_start = 0; /* disable in recursive calls */ - string_appendn (declp, *mangled, n); - } - *mangled += n; -} - -/* Extract a class name, possibly a template with arguments, from the - mangled string; qualifiers, local class indicators, etc. have - already been dealt with */ - -static int -demangle_class_name (work, mangled, declp) - struct work_stuff *work; - const char **mangled; - string *declp; -{ - int n; - int success = 0; - - n = consume_count (mangled); - if (n == -1) - return 0; - if ((int) strlen (*mangled) >= n) - { - demangle_arm_hp_template (work, mangled, n, declp); - success = 1; - } - - return (success); -} - -/* - -LOCAL FUNCTION - - demangle_class -- demangle a mangled class sequence - -SYNOPSIS - - static int - demangle_class (struct work_stuff *work, const char **mangled, - strint *declp) - -DESCRIPTION - - DECLP points to the buffer into which demangling is being done. - - *MANGLED points to the current token to be demangled. On input, - it points to a mangled class (I.E. "3foo", "13verylongclass", etc.) - On exit, it points to the next token after the mangled class on - success, or the first unconsumed token on failure. - - If the CONSTRUCTOR or DESTRUCTOR flags are set in WORK, then - we are demangling a constructor or destructor. In this case - we prepend "class::class" or "class::~class" to DECLP. - - Otherwise, we prepend "class::" to the current DECLP. - - Reset the constructor/destructor flags once they have been - "consumed". This allows demangle_class to be called later during - the same demangling, to do normal class demangling. - - Returns 1 if demangling is successful, 0 otherwise. - -*/ - -static int -demangle_class (work, mangled, declp) - struct work_stuff *work; - const char **mangled; - string *declp; -{ - int success = 0; - int btype; - string class_name; - char *save_class_name_end = 0; - - string_init (&class_name); - btype = register_Btype (work); - if (demangle_class_name (work, mangled, &class_name)) - { - save_class_name_end = class_name.p; - if ((work->constructor & 1) || (work->destructor & 1)) - { - /* adjust so we don't include template args */ - if (work->temp_start && (work->temp_start != -1)) - { - class_name.p = class_name.b + work->temp_start; - } - string_prepends (declp, &class_name); - if (work -> destructor & 1) - { - string_prepend (declp, "~"); - work -> destructor -= 1; - } - else - { - work -> constructor -= 1; - } - } - class_name.p = save_class_name_end; - remember_Ktype (work, class_name.b, LEN_STRING(&class_name)); - remember_Btype (work, class_name.b, LEN_STRING(&class_name), btype); - string_prepend (declp, SCOPE_STRING (work)); - string_prepends (declp, &class_name); - success = 1; - } - string_delete (&class_name); - return (success); -} - - -/* Called when there's a "__" in the mangled name, with `scan' pointing to - the rightmost guess. - - Find the correct "__"-sequence where the function name ends and the - signature starts, which is ambiguous with GNU mangling. - Call demangle_signature here, so we can make sure we found the right - one; *mangled will be consumed so caller will not make further calls to - demangle_signature. */ - -static int -iterate_demangle_function (work, mangled, declp, scan) - struct work_stuff *work; - const char **mangled; - string *declp; - const char *scan; -{ - const char *mangle_init = *mangled; - int success = 0; - string decl_init; - struct work_stuff work_init; - - if (*(scan + 2) == '\0') - return 0; - - /* Do not iterate for some demangling modes, or if there's only one - "__"-sequence. This is the normal case. */ - if (ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING - || strstr (scan + 2, "__") == NULL) - { - demangle_function_name (work, mangled, declp, scan); - return 1; - } - - /* Save state so we can restart if the guess at the correct "__" was - wrong. */ - string_init (&decl_init); - string_appends (&decl_init, declp); - memset (&work_init, 0, sizeof work_init); - work_stuff_copy_to_from (&work_init, work); - - /* Iterate over occurrences of __, allowing names and types to have a - "__" sequence in them. We must start with the first (not the last) - occurrence, since "__" most often occur between independent mangled - parts, hence starting at the last occurence inside a signature - might get us a "successful" demangling of the signature. */ - - while (scan[2]) - { - demangle_function_name (work, mangled, declp, scan); - success = demangle_signature (work, mangled, declp); - if (success) - break; - - /* Reset demangle state for the next round. */ - *mangled = mangle_init; - string_clear (declp); - string_appends (declp, &decl_init); - work_stuff_copy_to_from (work, &work_init); - - /* Leave this underscore-sequence. */ - scan += 2; - - /* Scan for the next "__" sequence. */ - while (*scan && (scan[0] != '_' || scan[1] != '_')) - scan++; - - /* Move to last "__" in this sequence. */ - while (*scan && *scan == '_') - scan++; - scan -= 2; - } - - /* Delete saved state. */ - delete_work_stuff (&work_init); - string_delete (&decl_init); - - return success; -} - -/* - -LOCAL FUNCTION - - demangle_prefix -- consume the mangled name prefix and find signature - -SYNOPSIS - - static int - demangle_prefix (struct work_stuff *work, const char **mangled, - string *declp); - -DESCRIPTION - - Consume and demangle the prefix of the mangled name. - While processing the function name root, arrange to call - demangle_signature if the root is ambiguous. - - DECLP points to the string buffer into which demangled output is - placed. On entry, the buffer is empty. On exit it contains - the root function name, the demangled operator name, or in some - special cases either nothing or the completely demangled result. - - MANGLED points to the current pointer into the mangled name. As each - token of the mangled name is consumed, it is updated. Upon entry - the current mangled name pointer points to the first character of - the mangled name. Upon exit, it should point to the first character - of the signature if demangling was successful, or to the first - unconsumed character if demangling of the prefix was unsuccessful. - - Returns 1 on success, 0 otherwise. - */ - -static int -demangle_prefix (work, mangled, declp) - struct work_stuff *work; - const char **mangled; - string *declp; -{ - int success = 1; - const char *scan; - int i; - - if (strlen(*mangled) > 6 - && (strncmp(*mangled, "_imp__", 6) == 0 - || strncmp(*mangled, "__imp_", 6) == 0)) - { - /* it's a symbol imported from a PE dynamic library. Check for both - new style prefix _imp__ and legacy __imp_ used by older versions - of dlltool. */ - (*mangled) += 6; - work->dllimported = 1; - } - else if (strlen(*mangled) >= 11 && strncmp(*mangled, "_GLOBAL_", 8) == 0) - { - char *marker = strchr (cplus_markers, (*mangled)[8]); - if (marker != NULL && *marker == (*mangled)[10]) - { - if ((*mangled)[9] == 'D') - { - /* it's a GNU global destructor to be executed at program exit */ - (*mangled) += 11; - work->destructor = 2; - if (gnu_special (work, mangled, declp)) - return success; - } - else if ((*mangled)[9] == 'I') - { - /* it's a GNU global constructor to be executed at program init */ - (*mangled) += 11; - work->constructor = 2; - if (gnu_special (work, mangled, declp)) - return success; - } - } - } - else if ((ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) && strncmp(*mangled, "__std__", 7) == 0) - { - /* it's a ARM global destructor to be executed at program exit */ - (*mangled) += 7; - work->destructor = 2; - } - else if ((ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) && strncmp(*mangled, "__sti__", 7) == 0) - { - /* it's a ARM global constructor to be executed at program initial */ - (*mangled) += 7; - work->constructor = 2; - } - - /* This block of code is a reduction in strength time optimization - of: - scan = strstr (*mangled, "__"); */ - - { - scan = *mangled; - - do { - scan = strchr (scan, '_'); - } while (scan != NULL && *++scan != '_'); - - if (scan != NULL) --scan; - } - - if (scan != NULL) - { - /* We found a sequence of two or more '_', ensure that we start at - the last pair in the sequence. */ - i = strspn (scan, "_"); - if (i > 2) - { - scan += (i - 2); - } - } - - if (scan == NULL) - { - success = 0; - } - else if (work -> static_type) - { - if (!g_ascii_isdigit ((unsigned char)scan[0]) && (scan[0] != 't')) - { - success = 0; - } - } - else if ((scan == *mangled) - && (g_ascii_isdigit ((unsigned char)scan[2]) || (scan[2] == 'Q') - || (scan[2] == 't') || (scan[2] == 'K') || (scan[2] == 'H'))) - { - /* The ARM says nothing about the mangling of local variables. - But cfront mangles local variables by prepending __ - to them. As an extension to ARM demangling we handle this case. */ - if ((LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING) - && g_ascii_isdigit ((unsigned char)scan[2])) - { - *mangled = scan + 2; - consume_count (mangled); - string_append (declp, *mangled); - *mangled += strlen (*mangled); - success = 1; - } - else - { - /* A GNU style constructor starts with __[0-9Qt]. But cfront uses - names like __Q2_3foo3bar for nested type names. So don't accept - this style of constructor for cfront demangling. A GNU - style member-template constructor starts with 'H'. */ - if (!(LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)) - work -> constructor += 1; - *mangled = scan + 2; - } - } - else if (ARM_DEMANGLING && scan[2] == 'p' && scan[3] == 't') - { - /* Cfront-style parameterized type. Handled later as a signature. */ - success = 1; - - /* ARM template? */ - demangle_arm_hp_template (work, mangled, strlen (*mangled), declp); - } - else if (EDG_DEMANGLING && ((scan[2] == 't' && scan[3] == 'm') - || (scan[2] == 'p' && scan[3] == 's') - || (scan[2] == 'p' && scan[3] == 't'))) - { - /* EDG-style parameterized type. Handled later as a signature. */ - success = 1; - - /* EDG template? */ - demangle_arm_hp_template (work, mangled, strlen (*mangled), declp); - } - else if ((scan == *mangled) && !g_ascii_isdigit ((unsigned char)scan[2]) - && (scan[2] != 't')) - { - /* Mangled name starts with "__". Skip over any leading '_' characters, - then find the next "__" that separates the prefix from the signature. - */ - if (!(ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) - || (arm_special (mangled, declp) == 0)) - { - while (*scan == '_') - { - scan++; - } - if ((scan = strstr (scan, "__")) == NULL || (*(scan + 2) == '\0')) - { - /* No separator (I.E. "__not_mangled"), or empty signature - (I.E. "__not_mangled_either__") */ - success = 0; - } - else - return iterate_demangle_function (work, mangled, declp, scan); - } - } - else if (*(scan + 2) != '\0') - { - /* Mangled name does not start with "__" but does have one somewhere - in there with non empty stuff after it. Looks like a global - function name. Iterate over all "__":s until the right - one is found. */ - return iterate_demangle_function (work, mangled, declp, scan); - } - else - { - /* Doesn't look like a mangled name */ - success = 0; - } - - if (!success && (work->constructor == 2 || work->destructor == 2)) - { - string_append (declp, *mangled); - *mangled += strlen (*mangled); - success = 1; - } - return (success); -} - -/* - -LOCAL FUNCTION - - gnu_special -- special handling of gnu mangled strings - -SYNOPSIS - - static int - gnu_special (struct work_stuff *work, const char **mangled, - string *declp); - - -DESCRIPTION - - Process some special GNU style mangling forms that don't fit - the normal pattern. For example: - - _$_3foo (destructor for class foo) - _vt$foo (foo virtual table) - _vt$foo$bar (foo::bar virtual table) - __vt_foo (foo virtual table, new style with thunks) - _3foo$varname (static data member) - _Q22rs2tu$vw (static data member) - __t6vector1Zii (constructor with template) - __thunk_4__$_7ostream (virtual function thunk) - */ - -static int -gnu_special (work, mangled, declp) - struct work_stuff *work; - const char **mangled; - string *declp; -{ - int n; - int success = 1; - const char *p; - - if ((*mangled)[0] == '_' - && strchr (cplus_markers, (*mangled)[1]) != NULL - && (*mangled)[2] == '_') - { - /* Found a GNU style destructor, get past "__" */ - (*mangled) += 3; - work -> destructor += 1; - } - else if ((*mangled)[0] == '_' - && (((*mangled)[1] == '_' - && (*mangled)[2] == 'v' - && (*mangled)[3] == 't' - && (*mangled)[4] == '_') - || ((*mangled)[1] == 'v' - && (*mangled)[2] == 't' - && strchr (cplus_markers, (*mangled)[3]) != NULL))) - { - /* Found a GNU style virtual table, get past "_vt" - and create the decl. Note that we consume the entire mangled - input string, which means that demangle_signature has no work - to do. */ - if ((*mangled)[2] == 'v') - (*mangled) += 5; /* New style, with thunks: "__vt_" */ - else - (*mangled) += 4; /* Old style, no thunks: "_vt" */ - while (**mangled != '\0') - { - switch (**mangled) - { - case 'Q': - case 'K': - success = demangle_qualified (work, mangled, declp, 0, 1); - break; - case 't': - success = demangle_template (work, mangled, declp, 0, 1, - 1); - break; - default: - if (g_ascii_isdigit((unsigned char)*mangled[0])) - { - n = consume_count(mangled); - /* We may be seeing a too-large size, or else a - "." indicating a static local symbol. In - any case, declare victory and move on; *don't* try - to use n to allocate. */ - if (n > (int) strlen (*mangled)) - { - success = 1; - break; - } - } - else - { - n = strcspn (*mangled, cplus_markers); - } - string_appendn (declp, *mangled, n); - (*mangled) += n; - } - - p = strpbrk (*mangled, cplus_markers); - if (success && ((p == NULL) || (p == *mangled))) - { - if (p != NULL) - { - string_append (declp, SCOPE_STRING (work)); - (*mangled)++; - } - } - else - { - success = 0; - break; - } - } - if (success) - string_append (declp, " virtual table"); - } - else if ((*mangled)[0] == '_' - && (strchr("0123456789Qt", (*mangled)[1]) != NULL) - && (p = strpbrk (*mangled, cplus_markers)) != NULL) - { - /* static data member, "_3foo$varname" for example */ - (*mangled)++; - switch (**mangled) - { - case 'Q': - case 'K': - success = demangle_qualified (work, mangled, declp, 0, 1); - break; - case 't': - success = demangle_template (work, mangled, declp, 0, 1, 1); - break; - default: - n = consume_count (mangled); - if (n < 0 || n > (long) strlen (*mangled)) - { - success = 0; - break; - } - - if (n > 10 && strncmp (*mangled, "_GLOBAL_", 8) == 0 - && (*mangled)[9] == 'N' - && (*mangled)[8] == (*mangled)[10] - && strchr (cplus_markers, (*mangled)[8])) - { - /* A member of the anonymous namespace. There's information - about what identifier or filename it was keyed to, but - it's just there to make the mangled name unique; we just - step over it. */ - string_append (declp, "{anonymous}"); - (*mangled) += n; - - /* Now p points to the marker before the N, so we need to - update it to the first marker after what we consumed. */ - p = strpbrk (*mangled, cplus_markers); - break; - } - - string_appendn (declp, *mangled, n); - (*mangled) += n; - } - if (success && (p == *mangled)) - { - /* Consumed everything up to the cplus_marker, append the - variable name. */ - (*mangled)++; - string_append (declp, SCOPE_STRING (work)); - n = strlen (*mangled); - string_appendn (declp, *mangled, n); - (*mangled) += n; - } - else - { - success = 0; - } - } - else if (strncmp (*mangled, "__thunk_", 8) == 0) - { - int delta; - - (*mangled) += 8; - delta = consume_count (mangled); - if (delta == -1) - success = 0; - else - { - char *method = internal_cplus_demangle (work, ++*mangled); - - if (method) - { - char buf[50]; - sprintf (buf, "virtual function thunk (delta:%d) for ", -delta); - string_append (declp, buf); - string_append (declp, method); - g_free (method); - n = strlen (*mangled); - (*mangled) += n; - } - else - { - success = 0; - } - } - } - else if (strncmp (*mangled, "__t", 3) == 0 - && ((*mangled)[3] == 'i' || (*mangled)[3] == 'f')) - { - p = (*mangled)[3] == 'i' ? " type_info node" : " type_info function"; - (*mangled) += 4; - switch (**mangled) - { - case 'Q': - case 'K': - success = demangle_qualified (work, mangled, declp, 0, 1); - break; - case 't': - success = demangle_template (work, mangled, declp, 0, 1, 1); - break; - default: - success = do_type (work, mangled, declp); - break; - } - if (success && **mangled != '\0') - success = 0; - if (success) - string_append (declp, p); - } - else - { - success = 0; - } - return (success); -} - -static void -recursively_demangle(work, mangled, result, namelength) - struct work_stuff *work; - const char **mangled; - string *result; - int namelength; -{ - char * recurse = (char *)NULL; - char * recurse_dem = (char *)NULL; - - recurse = (char *) g_malloc (namelength + 1); - memcpy (recurse, *mangled, namelength); - recurse[namelength] = '\000'; - - recurse_dem = sysprof_cplus_demangle (recurse, work->options); - - if (recurse_dem) - { - string_append (result, recurse_dem); - g_free (recurse_dem); - } - else - { - string_appendn (result, *mangled, namelength); - } - g_free (recurse); - *mangled += namelength; -} - -/* - -LOCAL FUNCTION - - arm_special -- special handling of ARM/lucid mangled strings - -SYNOPSIS - - static int - arm_special (const char **mangled, - string *declp); - - -DESCRIPTION - - Process some special ARM style mangling forms that don't fit - the normal pattern. For example: - - __vtbl__3foo (foo virtual table) - __vtbl__3foo__3bar (bar::foo virtual table) - - */ - -static int -arm_special (mangled, declp) - const char **mangled; - string *declp; -{ - int n; - int success = 1; - const char *scan; - - if (strncmp (*mangled, ARM_VTABLE_STRING, ARM_VTABLE_STRLEN) == 0) - { - /* Found a ARM style virtual table, get past ARM_VTABLE_STRING - and create the decl. Note that we consume the entire mangled - input string, which means that demangle_signature has no work - to do. */ - scan = *mangled + ARM_VTABLE_STRLEN; - while (*scan != '\0') /* first check it can be demangled */ - { - n = consume_count (&scan); - if (n == -1) - { - return (0); /* no good */ - } - scan += n; - if (scan[0] == '_' && scan[1] == '_') - { - scan += 2; - } - } - (*mangled) += ARM_VTABLE_STRLEN; - while (**mangled != '\0') - { - n = consume_count (mangled); - if (n == -1 - || n > (long) strlen (*mangled)) - return 0; - string_prependn (declp, *mangled, n); - (*mangled) += n; - if ((*mangled)[0] == '_' && (*mangled)[1] == '_') - { - string_prepend (declp, "::"); - (*mangled) += 2; - } - } - string_append (declp, " virtual table"); - } - else - { - success = 0; - } - return (success); -} - -/* - -LOCAL FUNCTION - - demangle_qualified -- demangle 'Q' qualified name strings - -SYNOPSIS - - static int - demangle_qualified (struct work_stuff *, const char *mangled, - string *result, int isfuncname, int append); - -DESCRIPTION - - Demangle a qualified name, such as "Q25Outer5Inner" which is - the mangled form of "Outer::Inner". The demangled output is - prepended or appended to the result string according to the - state of the append flag. - - If isfuncname is nonzero, then the qualified name we are building - is going to be used as a member function name, so if it is a - constructor or destructor function, append an appropriate - constructor or destructor name. I.E. for the above example, - the result for use as a constructor is "Outer::Inner::Inner" - and the result for use as a destructor is "Outer::Inner::~Inner". - -BUGS - - Numeric conversion is ASCII dependent (FIXME). - - */ - -static int -demangle_qualified (work, mangled, result, isfuncname, append) - struct work_stuff *work; - const char **mangled; - string *result; - int isfuncname; - int append; -{ - int qualifiers = 0; - int success = 1; - char num[2]; - string temp; - string last_name; - int bindex = register_Btype (work); - - /* We only make use of ISFUNCNAME if the entity is a constructor or - destructor. */ - isfuncname = (isfuncname - && ((work->constructor & 1) || (work->destructor & 1))); - - string_init (&temp); - string_init (&last_name); - - if ((*mangled)[0] == 'K') - { - /* Squangling qualified name reuse */ - int idx; - (*mangled)++; - idx = consume_count_with_underscores (mangled); - if (idx == -1 || idx >= work -> numk) - success = 0; - else - string_append (&temp, work -> ktypevec[idx]); - } - else - switch ((*mangled)[1]) - { - case '_': - /* GNU mangled name with more than 9 classes. The count is preceded - by an underscore (to distinguish it from the <= 9 case) and followed - by an underscore. */ - (*mangled)++; - qualifiers = consume_count_with_underscores (mangled); - if (qualifiers == -1) - success = 0; - break; - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - /* The count is in a single digit. */ - num[0] = (*mangled)[1]; - num[1] = '\0'; - qualifiers = atoi (num); - - /* If there is an underscore after the digit, skip it. This is - said to be for ARM-qualified names, but the ARM makes no - mention of such an underscore. Perhaps cfront uses one. */ - if ((*mangled)[2] == '_') - { - (*mangled)++; - } - (*mangled) += 2; - break; - - case '0': - default: - success = 0; - } - - if (!success) - return success; - - /* Pick off the names and collect them in the temp buffer in the order - in which they are found, separated by '::'. */ - - while (qualifiers-- > 0) - { - int remember_K = 1; - string_clear (&last_name); - - if (*mangled[0] == '_') - (*mangled)++; - - if (*mangled[0] == 't') - { - /* Here we always append to TEMP since we will want to use - the template name without the template parameters as a - constructor or destructor name. The appropriate - (parameter-less) value is returned by demangle_template - in LAST_NAME. We do not remember the template type here, - in order to match the G++ mangling algorithm. */ - success = demangle_template(work, mangled, &temp, - &last_name, 1, 0); - if (!success) - break; - } - else if (*mangled[0] == 'K') - { - int idx; - (*mangled)++; - idx = consume_count_with_underscores (mangled); - if (idx == -1 || idx >= work->numk) - success = 0; - else - string_append (&temp, work->ktypevec[idx]); - remember_K = 0; - - if (!success) break; - } - else - { - if (EDG_DEMANGLING) - { - int namelength; - /* Now recursively demangle the qualifier - * This is necessary to deal with templates in - * mangling styles like EDG */ - namelength = consume_count (mangled); - if (namelength == -1) - { - success = 0; - break; - } - recursively_demangle(work, mangled, &temp, namelength); - } - else - { - string_delete (&last_name); - success = do_type (work, mangled, &last_name); - if (!success) - break; - string_appends (&temp, &last_name); - } - } - - if (remember_K) - remember_Ktype (work, temp.b, LEN_STRING (&temp)); - - if (qualifiers > 0) - string_append (&temp, SCOPE_STRING (work)); - } - - remember_Btype (work, temp.b, LEN_STRING (&temp), bindex); - - /* If we are using the result as a function name, we need to append - the appropriate '::' separated constructor or destructor name. - We do this here because this is the most convenient place, where - we already have a pointer to the name and the length of the name. */ - - if (isfuncname) - { - string_append (&temp, SCOPE_STRING (work)); - if (work -> destructor & 1) - string_append (&temp, "~"); - string_appends (&temp, &last_name); - } - - /* Now either prepend the temp buffer to the result, or append it, - depending upon the state of the append flag. */ - - if (append) - string_appends (result, &temp); - else - { - if (!STRING_EMPTY (result)) - string_append (&temp, SCOPE_STRING (work)); - string_prepends (result, &temp); - } - - string_delete (&last_name); - string_delete (&temp); - return (success); -} - -/* - -LOCAL FUNCTION - - get_count -- convert an ascii count to integer, consuming tokens - -SYNOPSIS - - static int - get_count (const char **type, int *count) - -DESCRIPTION - - Assume that *type points at a count in a mangled name; set - *count to its value, and set *type to the next character after - the count. There are some weird rules in effect here. - - If *type does not point at a string of digits, return zero. - - If *type points at a string of digits followed by an - underscore, set *count to their value as an integer, advance - *type to point *after the underscore, and return 1. - - If *type points at a string of digits not followed by an - underscore, consume only the first digit. Set *count to its - value as an integer, leave *type pointing after that digit, - and return 1. - - The excuse for this odd behavior: in the ARM and HP demangling - styles, a type can be followed by a repeat count of the form - `Nxy', where: - - `x' is a single digit specifying how many additional copies - of the type to append to the argument list, and - - `y' is one or more digits, specifying the zero-based index of - the first repeated argument in the list. Yes, as you're - unmangling the name you can figure this out yourself, but - it's there anyway. - - So, for example, in `bar__3fooFPiN51', the first argument is a - pointer to an integer (`Pi'), and then the next five arguments - are the same (`N5'), and the first repeat is the function's - second argument (`1'). -*/ - -static int -get_count (type, count) - const char **type; - int *count; -{ - const char *p; - int n; - - if (!g_ascii_isdigit ((unsigned char)**type)) - return (0); - else - { - *count = **type - '0'; - (*type)++; - if (g_ascii_isdigit ((unsigned char)**type)) - { - p = *type; - n = *count; - do - { - n *= 10; - n += *p - '0'; - p++; - } - while (g_ascii_isdigit ((unsigned char)*p)); - if (*p == '_') - { - *type = p + 1; - *count = n; - } - } - } - return (1); -} - -/* RESULT will be initialised here; it will be freed on failure. The - value returned is really a type_kind_t. */ - -static int -do_type (work, mangled, result) - struct work_stuff *work; - const char **mangled; - string *result; -{ - int n; - int done; - int success; - string decl; - const char *remembered_type; - int type_quals; - type_kind_t tk = tk_none; - - string_init (&decl); - string_init (result); - - done = 0; - success = 1; - while (success && !done) - { - int member; - switch (**mangled) - { - - /* A pointer type */ - case 'P': - case 'p': - (*mangled)++; - if (! (work -> options & DMGL_JAVA)) - string_prepend (&decl, "*"); - if (tk == tk_none) - tk = tk_pointer; - break; - - /* A reference type */ - case 'R': - (*mangled)++; - string_prepend (&decl, "&"); - if (tk == tk_none) - tk = tk_reference; - break; - - /* An array */ - case 'A': - { - ++(*mangled); - if (!STRING_EMPTY (&decl) - && (decl.b[0] == '*' || decl.b[0] == '&')) - { - string_prepend (&decl, "("); - string_append (&decl, ")"); - } - string_append (&decl, "["); - if (**mangled != '_') - success = demangle_template_value_parm (work, mangled, &decl, - tk_integral); - if (**mangled == '_') - ++(*mangled); - string_append (&decl, "]"); - break; - } - - /* A back reference to a previously seen type */ - case 'T': - (*mangled)++; - if (!get_count (mangled, &n) || n >= work -> ntypes) - { - success = 0; - } - else - { - remembered_type = work -> typevec[n]; - mangled = &remembered_type; - } - break; - - /* A function */ - case 'F': - (*mangled)++; - if (!STRING_EMPTY (&decl) - && (decl.b[0] == '*' || decl.b[0] == '&')) - { - string_prepend (&decl, "("); - string_append (&decl, ")"); - } - /* After picking off the function args, we expect to either find the - function return type (preceded by an '_') or the end of the - string. */ - if (!demangle_nested_args (work, mangled, &decl) - || (**mangled != '_' && **mangled != '\0')) - { - success = 0; - break; - } - if (success && (**mangled == '_')) - (*mangled)++; - break; - - case 'M': - case 'O': - { - type_quals = TYPE_UNQUALIFIED; - - member = **mangled == 'M'; - (*mangled)++; - - string_append (&decl, ")"); - - /* We don't need to prepend `::' for a qualified name; - demangle_qualified will do that for us. */ - if (**mangled != 'Q') - string_prepend (&decl, SCOPE_STRING (work)); - - if (g_ascii_isdigit ((unsigned char)**mangled)) - { - n = consume_count (mangled); - if (n == -1 - || (int) strlen (*mangled) < n) - { - success = 0; - break; - } - string_prependn (&decl, *mangled, n); - *mangled += n; - } - else if (**mangled == 'X' || **mangled == 'Y') - { - string temp; - do_type (work, mangled, &temp); - string_prepends (&decl, &temp); - string_delete (&temp); - } - else if (**mangled == 't') - { - string temp; - string_init (&temp); - success = demangle_template (work, mangled, &temp, - NULL, 1, 1); - if (success) - { - string_prependn (&decl, temp.b, temp.p - temp.b); - string_delete (&temp); - } - else - break; - } - else if (**mangled == 'Q') - { - success = demangle_qualified (work, mangled, &decl, - /*isfuncnam=*/0, - /*append=*/0); - if (!success) - break; - } - else - { - success = 0; - break; - } - - string_prepend (&decl, "("); - if (member) - { - switch (**mangled) - { - case 'C': - case 'V': - case 'u': - type_quals |= code_for_qualifier (**mangled); - (*mangled)++; - break; - - default: - break; - } - - if (*(*mangled)++ != 'F') - { - success = 0; - break; - } - } - if ((member && !demangle_nested_args (work, mangled, &decl)) - || **mangled != '_') - { - success = 0; - break; - } - (*mangled)++; - if (! PRINT_ANSI_QUALIFIERS) - { - break; - } - if (type_quals != TYPE_UNQUALIFIED) - { - APPEND_BLANK (&decl); - string_append (&decl, qualifier_string (type_quals)); - } - break; - } - case 'G': - (*mangled)++; - break; - - case 'C': - case 'V': - case 'u': - if (PRINT_ANSI_QUALIFIERS) - { - if (!STRING_EMPTY (&decl)) - string_prepend (&decl, " "); - - string_prepend (&decl, demangle_qualifier (**mangled)); - } - (*mangled)++; - break; - /* - } - */ - - /* fall through */ - default: - done = 1; - break; - } - } - - if (success) switch (**mangled) - { - /* A qualified name, such as "Outer::Inner". */ - case 'Q': - case 'K': - { - success = demangle_qualified (work, mangled, result, 0, 1); - break; - } - - /* A back reference to a previously seen squangled type */ - case 'B': - (*mangled)++; - if (!get_count (mangled, &n) || n >= work -> numb) - success = 0; - else - string_append (result, work->btypevec[n]); - break; - - case 'X': - case 'Y': - /* A template parm. We substitute the corresponding argument. */ - { - int idx; - - (*mangled)++; - idx = consume_count_with_underscores (mangled); - - if (idx == -1 - || (work->tmpl_argvec && idx >= work->ntmpl_args) - || consume_count_with_underscores (mangled) == -1) - { - success = 0; - break; - } - - if (work->tmpl_argvec) - string_append (result, work->tmpl_argvec[idx]); - else - string_append_template_idx (result, idx); - - success = 1; - } - break; - - default: - success = demangle_fund_type (work, mangled, result); - if (tk == tk_none) - tk = (type_kind_t) success; - break; - } - - if (success) - { - if (!STRING_EMPTY (&decl)) - { - string_append (result, " "); - string_appends (result, &decl); - } - } - else - string_delete (result); - string_delete (&decl); - - if (success) - /* Assume an integral type, if we're not sure. */ - return (int) ((tk == tk_none) ? tk_integral : tk); - else - return 0; -} - -/* Given a pointer to a type string that represents a fundamental type - argument (int, long, unsigned int, etc) in TYPE, a pointer to the - string in which the demangled output is being built in RESULT, and - the WORK structure, decode the types and add them to the result. - - For example: - - "Ci" => "const int" - "Sl" => "signed long" - "CUs" => "const unsigned short" - - The value returned is really a type_kind_t. */ - -static int -demangle_fund_type (work, mangled, result) - struct work_stuff *work; - const char **mangled; - string *result; -{ - int done = 0; - int success = 1; - char buf[10]; - unsigned int dec = 0; - type_kind_t tk = tk_integral; - - /* First pick off any type qualifiers. There can be more than one. */ - - while (!done) - { - switch (**mangled) - { - case 'C': - case 'V': - case 'u': - if (PRINT_ANSI_QUALIFIERS) - { - if (!STRING_EMPTY (result)) - string_prepend (result, " "); - string_prepend (result, demangle_qualifier (**mangled)); - } - (*mangled)++; - break; - case 'U': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "unsigned"); - break; - case 'S': /* signed char only */ - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "signed"); - break; - case 'J': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "__complex"); - break; - default: - done = 1; - break; - } - } - - /* Now pick off the fundamental type. There can be only one. */ - - switch (**mangled) - { - case '\0': - case '_': - break; - case 'v': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "void"); - break; - case 'x': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "long long"); - break; - case 'l': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "long"); - break; - case 'i': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "int"); - break; - case 's': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "short"); - break; - case 'b': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "bool"); - tk = tk_bool; - break; - case 'c': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "char"); - tk = tk_char; - break; - case 'w': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "wchar_t"); - tk = tk_char; - break; - case 'r': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "long double"); - tk = tk_real; - break; - case 'd': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "double"); - tk = tk_real; - break; - case 'f': - (*mangled)++; - APPEND_BLANK (result); - string_append (result, "float"); - tk = tk_real; - break; - case 'G': - (*mangled)++; - if (!g_ascii_isdigit ((unsigned char)**mangled)) - { - success = 0; - break; - } - case 'I': - (*mangled)++; - if (**mangled == '_') - { - int i; - (*mangled)++; - for (i = 0; - i < (long) sizeof (buf) - 1 && **mangled && **mangled != '_'; - (*mangled)++, i++) - buf[i] = **mangled; - if (**mangled != '_') - { - success = 0; - break; - } - buf[i] = '\0'; - (*mangled)++; - } - else - { - strncpy (buf, *mangled, 2); - buf[2] = '\0'; - *mangled += min (strlen (*mangled), 2); - } - sscanf (buf, "%x", &dec); - sprintf (buf, "int%u_t", dec); - APPEND_BLANK (result); - string_append (result, buf); - break; - - /* fall through */ - /* An explicit type, such as "6mytype" or "7integer" */ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - int bindex = register_Btype (work); - string btype; - string_init (&btype); - if (demangle_class_name (work, mangled, &btype)) { - remember_Btype (work, btype.b, LEN_STRING (&btype), bindex); - APPEND_BLANK (result); - string_appends (result, &btype); - } - else - success = 0; - string_delete (&btype); - break; - } - case 't': - { - string btype; - string_init (&btype); - success = demangle_template (work, mangled, &btype, 0, 1, 1); - string_appends (result, &btype); - string_delete (&btype); - break; - } - default: - success = 0; - break; - } - - return success ? ((int) tk) : 0; -} - - -/* Handle a template's value parameter for HP aCC (extension from ARM) - **mangled points to 'S' or 'U' */ - -static int -do_hpacc_template_const_value (work, mangled, result) - struct work_stuff *work ATTRIBUTE_UNUSED; - const char **mangled; - string *result; -{ - int unsigned_const; - - if (**mangled != 'U' && **mangled != 'S') - return 0; - - unsigned_const = (**mangled == 'U'); - - (*mangled)++; - - switch (**mangled) - { - case 'N': - string_append (result, "-"); - /* fall through */ - case 'P': - (*mangled)++; - break; - case 'M': - /* special case for -2^31 */ - string_append (result, "-2147483648"); - (*mangled)++; - return 1; - default: - return 0; - } - - /* We have to be looking at an integer now */ - if (!(g_ascii_isdigit ((unsigned char)**mangled))) - return 0; - - /* We only deal with integral values for template - parameters -- so it's OK to look only for digits */ - while (g_ascii_isdigit ((unsigned char)**mangled)) - { - char_str[0] = **mangled; - string_append (result, char_str); - (*mangled)++; - } - - if (unsigned_const) - string_append (result, "U"); - - /* FIXME? Some day we may have 64-bit (or larger :-) ) constants - with L or LL suffixes. pai/1997-09-03 */ - - return 1; /* success */ -} - -/* Handle a template's literal parameter for HP aCC (extension from ARM) - **mangled is pointing to the 'A' */ - -static int -do_hpacc_template_literal (work, mangled, result) - struct work_stuff *work; - const char **mangled; - string *result; -{ - int literal_len = 0; - char * recurse; - char * recurse_dem; - - if (**mangled != 'A') - return 0; - - (*mangled)++; - - literal_len = consume_count (mangled); - - if (literal_len <= 0) - return 0; - - /* Literal parameters are names of arrays, functions, etc. and the - canonical representation uses the address operator */ - string_append (result, "&"); - - /* Now recursively demangle the literal name */ - recurse = (char *) g_malloc (literal_len + 1); - memcpy (recurse, *mangled, literal_len); - recurse[literal_len] = '\000'; - - recurse_dem = sysprof_cplus_demangle (recurse, work->options); - - if (recurse_dem) - { - string_append (result, recurse_dem); - g_free (recurse_dem); - } - else - { - string_appendn (result, *mangled, literal_len); - } - (*mangled) += literal_len; - g_free (recurse); - - return 1; -} - -static int -snarf_numeric_literal (args, arg) - const char ** args; - string * arg; -{ - if (**args == '-') - { - char_str[0] = '-'; - string_append (arg, char_str); - (*args)++; - } - else if (**args == '+') - (*args)++; - - if (!g_ascii_isdigit ((unsigned char)**args)) - return 0; - - while (g_ascii_isdigit ((unsigned char)**args)) - { - char_str[0] = **args; - string_append (arg, char_str); - (*args)++; - } - - return 1; -} - -/* Demangle the next argument, given by MANGLED into RESULT, which - *should be an uninitialized* string. It will be initialized here, - and free'd should anything go wrong. */ - -static int -do_arg (work, mangled, result) - struct work_stuff *work; - const char **mangled; - string *result; -{ - /* Remember where we started so that we can record the type, for - non-squangling type remembering. */ - const char *start = *mangled; - - string_init (result); - - if (work->nrepeats > 0) - { - --work->nrepeats; - - if (work->previous_argument == 0) - return 0; - - /* We want to reissue the previous type in this argument list. */ - string_appends (result, work->previous_argument); - return 1; - } - - if (**mangled == 'n') - { - /* A squangling-style repeat. */ - (*mangled)++; - work->nrepeats = consume_count(mangled); - - if (work->nrepeats <= 0) - /* This was not a repeat count after all. */ - return 0; - - if (work->nrepeats > 9) - { - if (**mangled != '_') - /* The repeat count should be followed by an '_' in this - case. */ - return 0; - else - (*mangled)++; - } - - /* Now, the repeat is all set up. */ - return do_arg (work, mangled, result); - } - - /* Save the result in WORK->previous_argument so that we can find it - if it's repeated. Note that saving START is not good enough: we - do not want to add additional types to the back-referenceable - type vector when processing a repeated type. */ - if (work->previous_argument) - string_delete (work->previous_argument); - else - work->previous_argument = (string*) g_malloc (sizeof (string)); - - if (!do_type (work, mangled, work->previous_argument)) - return 0; - - string_appends (result, work->previous_argument); - - remember_type (work, start, *mangled - start); - return 1; -} - -static void -remember_type (work, start, len) - struct work_stuff *work; - const char *start; - int len; -{ - char *tem; - - if (work->forgetting_types) - return; - - if (work -> ntypes >= work -> typevec_size) - { - if (work -> typevec_size == 0) - { - work -> typevec_size = 3; - work -> typevec - = (char **) g_malloc (sizeof (char *) * work -> typevec_size); - } - else - { - work -> typevec_size *= 2; - work -> typevec - = (char **) g_realloc ((char *)work -> typevec, - sizeof (char *) * work -> typevec_size); - } - } - tem = g_malloc (len + 1); - memcpy (tem, start, len); - tem[len] = '\0'; - work -> typevec[work -> ntypes++] = tem; -} - - -/* Remember a K type class qualifier. */ -static void -remember_Ktype (work, start, len) - struct work_stuff *work; - const char *start; - int len; -{ - char *tem; - - if (work -> numk >= work -> ksize) - { - if (work -> ksize == 0) - { - work -> ksize = 5; - work -> ktypevec - = (char **) g_malloc (sizeof (char *) * work -> ksize); - } - else - { - work -> ksize *= 2; - work -> ktypevec - = (char **) g_realloc ((char *)work -> ktypevec, - sizeof (char *) * work -> ksize); - } - } - tem = g_malloc (len + 1); - memcpy (tem, start, len); - tem[len] = '\0'; - work -> ktypevec[work -> numk++] = tem; -} - -/* Register a B code, and get an index for it. B codes are registered - as they are seen, rather than as they are completed, so map > - registers map > as B0, and temp as B1 */ - -static int -register_Btype (work) - struct work_stuff *work; -{ - int ret; - - if (work -> numb >= work -> bsize) - { - if (work -> bsize == 0) - { - work -> bsize = 5; - work -> btypevec - = (char **) g_malloc (sizeof (char *) * work -> bsize); - } - else - { - work -> bsize *= 2; - work -> btypevec - = (char **) g_realloc ((char *)work -> btypevec, - sizeof (char *) * work -> bsize); - } - } - ret = work -> numb++; - work -> btypevec[ret] = NULL; - return(ret); -} - -/* Store a value into a previously registered B code type. */ - -static void -remember_Btype (work, start, len, index) - struct work_stuff *work; - const char *start; - int len, index; -{ - char *tem; - - tem = g_malloc (len + 1); - memcpy (tem, start, len); - tem[len] = '\0'; - work -> btypevec[index] = tem; -} - -/* Lose all the info related to B and K type codes. */ -static void -forget_B_and_K_types (work) - struct work_stuff *work; -{ - int i; - - while (work -> numk > 0) - { - i = --(work -> numk); - if (work -> ktypevec[i] != NULL) - { - g_free (work -> ktypevec[i]); - work -> ktypevec[i] = NULL; - } - } - - while (work -> numb > 0) - { - i = --(work -> numb); - if (work -> btypevec[i] != NULL) - { - g_free (work -> btypevec[i]); - work -> btypevec[i] = NULL; - } - } -} -/* Forget the remembered types, but not the type vector itself. */ - -static void -forget_types (work) - struct work_stuff *work; -{ - int i; - - while (work -> ntypes > 0) - { - i = --(work -> ntypes); - if (work -> typevec[i] != NULL) - { - g_free (work -> typevec[i]); - work -> typevec[i] = NULL; - } - } -} - -/* Process the argument list part of the signature, after any class spec - has been consumed, as well as the first 'F' character (if any). For - example: - - "__als__3fooRT0" => process "RT0" - "complexfunc5__FPFPc_PFl_i" => process "PFPc_PFl_i" - - DECLP must be already initialised, usually non-empty. It won't be freed - on failure. - - Note that g++ differs significantly from ARM and lucid style mangling - with regards to references to previously seen types. For example, given - the source fragment: - - class foo { - public: - foo::foo (int, foo &ia, int, foo &ib, int, foo &ic); - }; - - foo::foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; } - void foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; } - - g++ produces the names: - - __3fooiRT0iT2iT2 - foo__FiR3fooiT1iT1 - - while lcc (and presumably other ARM style compilers as well) produces: - - foo__FiR3fooT1T2T1T2 - __ct__3fooFiR3fooT1T2T1T2 - - Note that g++ bases its type numbers starting at zero and counts all - previously seen types, while lucid/ARM bases its type numbers starting - at one and only considers types after it has seen the 'F' character - indicating the start of the function args. For lucid/ARM style, we - account for this difference by discarding any previously seen types when - we see the 'F' character, and subtracting one from the type number - reference. - - */ - -static int -demangle_args (work, mangled, declp) - struct work_stuff *work; - const char **mangled; - string *declp; -{ - string arg; - int need_comma = 0; - int r; - int t; - const char *tem; - char temptype; - - if (PRINT_ARG_TYPES) - { - string_append (declp, "("); - if (**mangled == '\0') - { - string_append (declp, "void"); - } - } - - while ((**mangled != '_' && **mangled != '\0' && **mangled != 'e') - || work->nrepeats > 0) - { - if ((**mangled == 'N') || (**mangled == 'T')) - { - temptype = *(*mangled)++; - - if (temptype == 'N') - { - if (!get_count (mangled, &r)) - { - return (0); - } - } - else - { - r = 1; - } - if ((HP_DEMANGLING || ARM_DEMANGLING || EDG_DEMANGLING) && work -> ntypes >= 10) - { - /* If we have 10 or more types we might have more than a 1 digit - index so we'll have to consume the whole count here. This - will lose if the next thing is a type name preceded by a - count but it's impossible to demangle that case properly - anyway. Eg if we already have 12 types is T12Pc "(..., type1, - Pc, ...)" or "(..., type12, char *, ...)" */ - if ((t = consume_count(mangled)) <= 0) - { - return (0); - } - } - else - { - if (!get_count (mangled, &t)) - { - return (0); - } - } - if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) - { - t--; - } - /* Validate the type index. Protect against illegal indices from - malformed type strings. */ - if ((t < 0) || (t >= work -> ntypes)) - { - return (0); - } - while (work->nrepeats > 0 || --r >= 0) - { - tem = work -> typevec[t]; - if (need_comma && PRINT_ARG_TYPES) - { - string_append (declp, ", "); - } - if (!do_arg (work, &tem, &arg)) - { - return (0); - } - if (PRINT_ARG_TYPES) - { - string_appends (declp, &arg); - } - string_delete (&arg); - need_comma = 1; - } - } - else - { - if (need_comma && PRINT_ARG_TYPES) - string_append (declp, ", "); - if (!do_arg (work, mangled, &arg)) - return (0); - if (PRINT_ARG_TYPES) - string_appends (declp, &arg); - string_delete (&arg); - need_comma = 1; - } - } - - if (**mangled == 'e') - { - (*mangled)++; - if (PRINT_ARG_TYPES) - { - if (need_comma) - { - string_append (declp, ","); - } - string_append (declp, "..."); - } - } - - if (PRINT_ARG_TYPES) - { - string_append (declp, ")"); - } - return (1); -} - -/* Like demangle_args, but for demangling the argument lists of function - and method pointers or references, not top-level declarations. */ - -static int -demangle_nested_args (work, mangled, declp) - struct work_stuff *work; - const char **mangled; - string *declp; -{ - string* saved_previous_argument; - int result; - int saved_nrepeats; - - /* The G++ name-mangling algorithm does not remember types on nested - argument lists, unless -fsquangling is used, and in that case the - type vector updated by remember_type is not used. So, we turn - off remembering of types here. */ - ++work->forgetting_types; - - /* For the repeat codes used with -fsquangling, we must keep track of - the last argument. */ - saved_previous_argument = work->previous_argument; - saved_nrepeats = work->nrepeats; - work->previous_argument = 0; - work->nrepeats = 0; - - /* Actually demangle the arguments. */ - result = demangle_args (work, mangled, declp); - - /* Restore the previous_argument field. */ - if (work->previous_argument) - { - string_delete (work->previous_argument); - g_free ((char *) work->previous_argument); - } - work->previous_argument = saved_previous_argument; - --work->forgetting_types; - work->nrepeats = saved_nrepeats; - - return result; -} - -static void -demangle_function_name (work, mangled, declp, scan) - struct work_stuff *work; - const char **mangled; - string *declp; - const char *scan; -{ - size_t i; - string type; - const char *tem; - - string_appendn (declp, (*mangled), scan - (*mangled)); - string_need (declp, 1); - *(declp -> p) = '\0'; - - /* Consume the function name, including the "__" separating the name - from the signature. We are guaranteed that SCAN points to the - separator. */ - - (*mangled) = scan + 2; - /* We may be looking at an instantiation of a template function: - foo__Xt1t2_Ft3t4, where t1, t2, ... are template arguments and a - following _F marks the start of the function arguments. Handle - the template arguments first. */ - - if (HP_DEMANGLING && (**mangled == 'X')) - { - demangle_arm_hp_template (work, mangled, 0, declp); - /* This leaves MANGLED pointing to the 'F' marking func args */ - } - - if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) - { - - /* See if we have an ARM style constructor or destructor operator. - If so, then just record it, clear the decl, and return. - We can't build the actual constructor/destructor decl until later, - when we recover the class name from the signature. */ - - if (strcmp (declp -> b, "__ct") == 0) - { - work -> constructor += 1; - string_clear (declp); - return; - } - else if (strcmp (declp -> b, "__dt") == 0) - { - work -> destructor += 1; - string_clear (declp); - return; - } - } - - if (declp->p - declp->b >= 3 - && declp->b[0] == 'o' - && declp->b[1] == 'p' - && strchr (cplus_markers, declp->b[2]) != NULL) - { - /* see if it's an assignment expression */ - if (declp->p - declp->b >= 10 /* op$assign_ */ - && memcmp (declp->b + 3, "assign_", 7) == 0) - { - for (i = 0; i < G_N_ELEMENTS (optable); i++) - { - int len = declp->p - declp->b - 10; - if ((int) strlen (optable[i].in) == len - && memcmp (optable[i].in, declp->b + 10, len) == 0) - { - string_clear (declp); - string_append (declp, "operator"); - string_append (declp, optable[i].out); - string_append (declp, "="); - break; - } - } - } - else - { - for (i = 0; i < G_N_ELEMENTS (optable); i++) - { - int len = declp->p - declp->b - 3; - if ((int) strlen (optable[i].in) == len - && memcmp (optable[i].in, declp->b + 3, len) == 0) - { - string_clear (declp); - string_append (declp, "operator"); - string_append (declp, optable[i].out); - break; - } - } - } - } - else if (declp->p - declp->b >= 5 && memcmp (declp->b, "type", 4) == 0 - && strchr (cplus_markers, declp->b[4]) != NULL) - { - /* type conversion operator */ - tem = declp->b + 5; - if (do_type (work, &tem, &type)) - { - string_clear (declp); - string_append (declp, "operator "); - string_appends (declp, &type); - string_delete (&type); - } - } - else if (declp->b[0] == '_' && declp->b[1] == '_' - && declp->b[2] == 'o' && declp->b[3] == 'p') - { - /* ANSI. */ - /* type conversion operator. */ - tem = declp->b + 4; - if (do_type (work, &tem, &type)) - { - string_clear (declp); - string_append (declp, "operator "); - string_appends (declp, &type); - string_delete (&type); - } - } - else if (declp->b[0] == '_' && declp->b[1] == '_' - && g_ascii_islower((unsigned char)declp->b[2]) - && g_ascii_islower((unsigned char)declp->b[3])) - { - if (declp->b[4] == '\0') - { - /* Operator. */ - for (i = 0; i < G_N_ELEMENTS (optable); i++) - { - if (strlen (optable[i].in) == 2 - && memcmp (optable[i].in, declp->b + 2, 2) == 0) - { - string_clear (declp); - string_append (declp, "operator"); - string_append (declp, optable[i].out); - break; - } - } - } - else - { - if (declp->b[2] == 'a' && declp->b[5] == '\0') - { - /* Assignment. */ - for (i = 0; i < G_N_ELEMENTS (optable); i++) - { - if (strlen (optable[i].in) == 3 - && memcmp (optable[i].in, declp->b + 2, 3) == 0) - { - string_clear (declp); - string_append (declp, "operator"); - string_append (declp, optable[i].out); - break; - } - } - } - } - } -} - -/* a mini string-handling package */ - -static void -string_need (s, n) - string *s; - int n; -{ - int tem; - - if (s->b == NULL) - { - if (n < 32) - { - n = 32; - } - s->p = s->b = g_malloc (n); - s->e = s->b + n; - } - else if (s->e - s->p < n) - { - tem = s->p - s->b; - n += tem; - n *= 2; - s->b = g_realloc (s->b, n); - s->p = s->b + tem; - s->e = s->b + n; - } -} - -static void -string_delete (s) - string *s; -{ - if (s->b != NULL) - { - g_free (s->b); - s->b = s->e = s->p = NULL; - } -} - -static void -string_init (s) - string *s; -{ - s->b = s->p = s->e = NULL; -} - -static void -string_clear (s) - string *s; -{ - s->p = s->b; -} - -#if 0 - -static int -string_empty (s) - string *s; -{ - return (s->b == s->p); -} - -#endif - -static void -string_append (p, s) - string *p; - const char *s; -{ - int n; - if (s == NULL || *s == '\0') - return; - n = strlen (s); - string_need (p, n); - memcpy (p->p, s, n); - p->p += n; -} - -static void -string_appends (p, s) - string *p, *s; -{ - int n; - - if (s->b != s->p) - { - n = s->p - s->b; - string_need (p, n); - memcpy (p->p, s->b, n); - p->p += n; - } -} - -static void -string_appendn (p, s, n) - string *p; - const char *s; - int n; -{ - if (n != 0) - { - string_need (p, n); - memcpy (p->p, s, n); - p->p += n; - } -} - -static void -string_prepend (p, s) - string *p; - const char *s; -{ - if (s != NULL && *s != '\0') - { - string_prependn (p, s, strlen (s)); - } -} - -static void -string_prepends (p, s) - string *p, *s; -{ - if (s->b != s->p) - { - string_prependn (p, s->b, s->p - s->b); - } -} - -static void -string_prependn (p, s, n) - string *p; - const char *s; - int n; -{ - char *q; - - if (n != 0) - { - string_need (p, n); - for (q = p->p - 1; q >= p->b; q--) - { - q[n] = q[0]; - } - memcpy (p->b, s, n); - p->p += n; - } -} - -static void -string_append_template_idx (s, idx) - string *s; - int idx; -{ - char buf[INTBUF_SIZE + 1 /* 'T' */]; - sprintf(buf, "T%d", idx); - string_append (s, buf); -} diff --git a/lib/elfparser.c b/lib/elfparser.c deleted file mode 100644 index d10fce7d..00000000 --- a/lib/elfparser.c +++ /dev/null @@ -1,814 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2006, 2007, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#include -#include -#include -#include -#include "elfparser.h" - -typedef struct Section Section; - -struct ElfSym -{ - gulong table; - gulong offset; - gulong address; -}; - -struct Section -{ - const gchar * name; - gsize offset; - gsize size; - gboolean allocated; - gulong load_address; - guint type; -}; - -struct ElfParser -{ - gboolean is_64; - const guchar * data; - gsize length; - - int n_sections; - Section ** sections; - - int n_symbols; - ElfSym * symbols; - gsize sym_strings; - - GMappedFile * file; - - char * filename; - - gboolean checked_build_id; - char * build_id; - - const Section * text_section; -}; - -/* FIXME: All of these should in principle do endian swapping, - * but sysprof never has to deal with binaries of a different - * endianness than sysprof itself - */ -#define GET_FIELD(parser, offset, struct_name, idx, field_name) \ - (((parser))->is_64? \ - ((Elf64_ ## struct_name *)(((parser)->data + offset)) + (idx))->field_name : \ - ((Elf32_ ## struct_name *)(((parser)->data + offset)) + (idx))->field_name) - -#define GET_UINT32(parser, offset) \ - *((uint32_t *)(parser->data + offset)) \ - -#define GET_SIZE(parser, struct_name) \ - (((parser)->is_64? \ - sizeof (Elf64_ ## struct_name) : \ - sizeof (Elf32_ ## struct_name))) - -#define MAKE_ELF_UINT_ACCESSOR(field_name) \ - static uint64_t field_name (ElfParser *parser) \ - { \ - return GET_FIELD (parser, 0, Ehdr, 0, field_name); \ - } - -MAKE_ELF_UINT_ACCESSOR (e_shoff) -MAKE_ELF_UINT_ACCESSOR (e_shnum) -MAKE_ELF_UINT_ACCESSOR (e_shstrndx) - -#define MAKE_SECTION_HEADER_ACCESSOR(field_name) \ - static uint64_t field_name (ElfParser *parser, int nth_section) \ - { \ - gsize offset = e_shoff (parser); \ - \ - return GET_FIELD (parser, offset, Shdr, nth_section, field_name); \ - } - -MAKE_SECTION_HEADER_ACCESSOR (sh_name); -MAKE_SECTION_HEADER_ACCESSOR (sh_type); -MAKE_SECTION_HEADER_ACCESSOR (sh_flags); -MAKE_SECTION_HEADER_ACCESSOR (sh_addr); -MAKE_SECTION_HEADER_ACCESSOR (sh_offset); -MAKE_SECTION_HEADER_ACCESSOR (sh_size); - -#define MAKE_SYMBOL_ACCESSOR(field_name) \ - static uint64_t field_name (ElfParser *parser, gulong offset, gulong nth) \ - { \ - return GET_FIELD (parser, offset, Sym, nth, field_name); \ - } - -MAKE_SYMBOL_ACCESSOR(st_name); -MAKE_SYMBOL_ACCESSOR(st_info); -MAKE_SYMBOL_ACCESSOR(st_value); -MAKE_SYMBOL_ACCESSOR(st_size); -MAKE_SYMBOL_ACCESSOR(st_shndx); - -static void -section_free (Section *section) -{ - g_free (section); -} - -static const Section * -find_section (ElfParser *parser, - const char *name, - guint type) -{ - int i; - - for (i = 0; i < parser->n_sections; ++i) - { - Section *section = parser->sections[i]; - - if (strcmp (section->name, name) == 0 && section->type == type) - return section; - } - - return NULL; -} - -static gboolean -parse_elf_signature (const guchar *data, - gsize length, - gboolean *is_64, - gboolean *is_be) -{ - /* FIXME: this function should be able to return an error */ - if (length < EI_NIDENT) - { - /* FIXME set error */ - return FALSE; - } - - if (data[EI_CLASS] != ELFCLASS32 && - data[EI_CLASS] != ELFCLASS64) - { - /* FIXME set error */ - return FALSE; - } - - if (data[EI_DATA] != ELFDATA2LSB && - data[EI_DATA] != ELFDATA2MSB) - { - /* FIXME set error */ - return FALSE; - } - - if (is_64) - *is_64 = (data[EI_CLASS] == ELFCLASS64); - - if (is_be) - *is_be = (data[EI_DATA] == ELFDATA2MSB); - - return TRUE; -} - -ElfParser * -elf_parser_new_from_data (const guchar *data, - gsize length) -{ - ElfParser *parser; - gboolean is_64, is_big_endian; - int section_names_idx; - const guchar *section_names; - gsize section_headers; - int i; - - if (!parse_elf_signature (data, length, &is_64, &is_big_endian)) - { - /* FIXME: set error */ - return NULL; - } - - parser = g_new0 (ElfParser, 1); - - parser->is_64 = is_64; - parser->data = data; - parser->length = length; - -#if 0 - g_print (" new parser : %p\n", parser); -#endif - - /* Read ELF header */ - - parser->n_sections = e_shnum (parser); - section_names_idx = e_shstrndx (parser); - section_headers = e_shoff (parser); - - /* Read section headers */ - parser->sections = g_new0 (Section *, parser->n_sections); - - section_names = parser->data + sh_offset (parser, section_names_idx); - - for (i = 0; i < parser->n_sections; ++i) - { - Section *section = g_new (Section, 1); - - section->name = (char *)(section_names + sh_name (parser, i)); - section->size = sh_size (parser, i); - section->offset = sh_offset (parser, i); - section->allocated = !!(sh_flags (parser, i) & SHF_ALLOC); - - if (section->allocated) - section->load_address = sh_addr (parser, i); - else - section->load_address = 0; - - section->type = sh_type (parser, i); - - parser->sections[i] = section; - } - - /* Cache the text section */ - parser->text_section = find_section (parser, ".text", SHT_PROGBITS); - if (!parser->text_section) - parser->text_section = find_section (parser, ".text", SHT_NOBITS); - - parser->filename = NULL; - parser->build_id = NULL; - - return parser; -} - -ElfParser * -elf_parser_new (const char *filename, - GError **err) -{ - const guchar *data; - gsize length; - ElfParser *parser; - - GMappedFile *file = g_mapped_file_new (filename, FALSE, NULL); - - if (!file) - return NULL; - -#if 0 - g_print ("elf parser new : %s\n", filename); -#endif - - data = (guchar *)g_mapped_file_get_contents (file); - length = g_mapped_file_get_length (file); - -#if 0 - g_print ("data %p: for %s\n", data, filename); -#endif - - parser = elf_parser_new_from_data (data, length); - -#if 0 - g_print ("Parser for %s: %p\n", filename, parser); -#endif - - if (!parser) - { - g_mapped_file_free (file); - return NULL; - } - - parser->filename = g_strdup (filename); - - parser->file = file; - -#if 0 - g_print ("Elf file: %s (debug: %s)\n", - filename, elf_parser_get_debug_link (parser, NULL)); - - if (!parser->symbols) - g_print ("at this point %s has no symbols\n", filename); -#endif - - return parser; -} - -guint32 -elf_parser_get_crc32 (ElfParser *parser) -{ - static const unsigned long crc32_table[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d - }; - const guchar *data; - gsize length; - gulong crc; - gsize i; - - data = parser->data; - length = parser->length; - - crc = 0xffffffff; - - madvise ((char *)data, length, MADV_SEQUENTIAL); - - for (i = 0; i < length; ++i) - crc = crc32_table[(crc ^ data[i]) & 0xff] ^ (crc >> 8); - - /* We just read the entire file into memory, but we only really - * need the symbol table, so swap the whole thing out. - * - * We could be more exact here, but it's only a few minor - * pagefaults. - */ - if (parser->file) - madvise ((char *)data, length, MADV_DONTNEED); - - return ~crc & 0xffffffff; -} - -void -elf_parser_free (ElfParser *parser) -{ - int i; - - for (i = 0; i < parser->n_sections; ++i) - section_free (parser->sections[i]); - g_free (parser->sections); - - if (parser->file) - g_mapped_file_free (parser->file); - - g_free (parser->symbols); - - if (parser->filename) - g_free (parser->filename); - - if (parser->build_id) - g_free (parser->build_id); - - g_free (parser); -} - -extern char *sysprof_cplus_demangle (const char *name, int options); - -char * -elf_demangle (const char *name) -{ -#define DMGL_PARAMS (1 << 0) /* Include function args */ -#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ - - char *demangled = sysprof_cplus_demangle (name, DMGL_PARAMS | DMGL_ANSI); - - if (demangled) - return demangled; - else - return g_strdup (name); -} - -/* - * Looking up symbols - */ -static int -compare_sym (const void *a, const void *b) -{ - const ElfSym *sym_a = a; - const ElfSym *sym_b = b; - - if (sym_a->address < sym_b->address) - return -1; - else if (sym_a->address == sym_b->address) - return 0; - else - return 1; -} - -#if 0 -static void -dump_symbols (ElfParser *parser, ElfSym *syms, guint n_syms) -{ - int i; - - for (i = 0; i < n_syms; ++i) - { - ElfSym *s = &(syms[i]); - - g_print (" %s: %lx\n", elf_parser_get_sym_name (parser, s), s->address); - } -} -#endif - -static void -read_table (ElfParser *parser, - const Section *sym_table, - const Section *str_table) -{ - int sym_size = GET_SIZE (parser, Sym); - int i, n_symbols; - -#if 0 - g_print ("elf: Reading table for %s\n", parser->filename? parser->filename : ""); -#endif - - parser->n_symbols = sym_table->size / sym_size; - parser->symbols = g_new (ElfSym, parser->n_symbols); - -#if 0 - g_print ("sym table offset: %d\n", sym_table->offset); -#endif - - n_symbols = 0; -#if 0 - g_print ("n syms: %d\n", parser->n_symbols); -#endif - for (i = 0; i < parser->n_symbols; ++i) - { - guint info; - gulong addr; - gulong shndx; - - info = st_info (parser, sym_table->offset, i); - addr = st_value (parser, sym_table->offset, i); - shndx = st_shndx (parser, sym_table->offset, i); - -#if 0 - g_print ("read symbol: %s (section: %d)\n", get_string_indirct (parser->parser, - parser->sym_format, "st_name", - str_table->offset), - shndx); -#endif - - if (addr != 0 && - shndx < parser->n_sections && - parser->sections[shndx] == parser->text_section && - (info & 0xf) == STT_FUNC && - ((info >> 4) == STB_GLOBAL || - (info >> 4) == STB_LOCAL || - (info >> 4) == STB_WEAK)) - { - parser->symbols[n_symbols].address = addr; - parser->symbols[n_symbols].table = sym_table->offset; - parser->symbols[n_symbols].offset = i; - - n_symbols++; - -#if 0 - g_print (" symbol: %s: %lx\n", - get_string_indirect (parser->parser, - parser->sym_format, "st_name", - str_table->offset), - addr - parser->text_section->load_address); - g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n", - addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); -#endif - } - else if (addr != 0) - { -#if 0 - g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n", - addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); -#endif - } - } - - parser->sym_strings = str_table->offset; - parser->n_symbols = n_symbols; - - /* Allocate space for at least one symbol, so that parser->symbols will be - * non-NULL. If it ends up being NULL, we will be parsing the file over and - * over. - */ - parser->symbols = g_renew (ElfSym, parser->symbols, parser->n_symbols + 1); - - qsort (parser->symbols, parser->n_symbols, sizeof (ElfSym), compare_sym); -} - -static void -read_symbols (ElfParser *parser) -{ - const Section *symtab = find_section (parser, ".symtab", SHT_SYMTAB); - const Section *strtab = find_section (parser, ".strtab", SHT_STRTAB); - const Section *dynsym = find_section (parser, ".dynsym", SHT_DYNSYM); - const Section *dynstr = find_section (parser, ".dynstr", SHT_STRTAB); - - if (symtab && strtab) - { -#if 0 - g_print ("reading symbol table of %s\n", parser->filename); -#endif - read_table (parser, symtab, strtab); - } - else if (dynsym && dynstr) - { -#if 0 - g_print ("reading dynamic symbol table of %s\n", parser->filename); -#endif - read_table (parser, dynsym, dynstr); - } - else - { - /* To make sure parser->symbols is non-NULL */ - parser->n_symbols = 0; - parser->symbols = g_new (ElfSym, 1); - } -} - -static ElfSym * -do_lookup (ElfSym *symbols, - gulong address, - int first, - int last) -{ - if (address >= symbols[last].address) - { - return &(symbols[last]); - } - else if (last - first < 3) - { - while (last >= first) - { - if (address >= symbols[last].address) - return &(symbols[last]); - - last--; - } - - return NULL; - } - else - { - int mid = (first + last) / 2; - - if (symbols[mid].address > address) - return do_lookup (symbols, address, first, mid); - else - return do_lookup (symbols, address, mid, last); - } -} - -/* Address should be given in 'offset into text segment' */ -const ElfSym * -elf_parser_lookup_symbol (ElfParser *parser, - gulong address) -{ - const ElfSym *result; - - if (!parser->symbols) - { -#if 0 - g_print ("reading symbols at %p\n", parser); -#endif - read_symbols (parser); - } - - if (parser->n_symbols == 0) - return NULL; - - if (!parser->text_section) - return NULL; - - address += parser->text_section->load_address; - -#if 0 - g_print ("elf: the address we are looking up is %p\n", address); -#endif - - result = do_lookup (parser->symbols, address, 0, parser->n_symbols - 1); - -#if 0 - if (result) - { - g_print (" elf: found %s at %lx\n", elf_parser_get_sym_name (parser, result), result->address); - } - else - { - g_print ("elf: not found\n"); - } -#endif - - if (result) - { - gulong size = st_size (parser, result->table, result->offset); - - if (size > 0 && result->address + size <= address) - { -#if 0 - g_print (" elf: ends at %lx, so rejecting\n", - result->address + size); -#endif - result = NULL; - } - } - - if (result) - { - /* Reject the symbols if the address is outside the text section */ - if (address > parser->text_section->load_address + parser->text_section->size) - result = NULL; - } - - return result; -} - -gulong -elf_parser_get_text_offset (ElfParser *parser) -{ - g_return_val_if_fail (parser != NULL, (gulong)-1); - - if (!parser->text_section) - return (gulong)-1; - - return parser->text_section->offset; -} - -static gchar * -make_hex_string (const guchar *data, int n_bytes) -{ - static const char hex_digits[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - GString *string = g_string_new (NULL); - int i; - - for (i = 0; i < n_bytes; ++i) - { - char c = data[i]; - - g_string_append_c (string, hex_digits[(c & 0xf0) >> 4]); - g_string_append_c (string, hex_digits[(c & 0x0f)]); - } - - return g_string_free (string, FALSE); -} - -const gchar * -elf_parser_get_build_id (ElfParser *parser) -{ - if (!parser->checked_build_id) - { - const Section *build_id = - find_section (parser, ".note.gnu.build-id", SHT_NOTE); - guint64 name_size; - guint64 desc_size; - guint64 type; - const char *name; - guint64 offset; - - parser->checked_build_id = TRUE; - - if (!build_id) - return NULL; - - offset = build_id->offset; - - name_size = GET_FIELD (parser, offset, Nhdr, 0, n_namesz); - desc_size = GET_FIELD (parser, offset, Nhdr, 0, n_descsz); - type = GET_FIELD (parser, offset, Nhdr, 0, n_type); - - offset += GET_SIZE (parser, Nhdr); - - name = (char *)(parser->data + offset); - - if (strncmp (name, ELF_NOTE_GNU, name_size) != 0 || type != NT_GNU_BUILD_ID) - return NULL; - - offset += strlen (name); - - offset = (offset + 3) & (~0x3); - - parser->build_id = make_hex_string (parser->data + offset, desc_size); - } - - return parser->build_id; -} - -const char * -elf_parser_get_debug_link (ElfParser *parser, guint32 *crc32) -{ - guint64 offset; - const Section *debug_link = find_section (parser, ".gnu_debuglink", - SHT_PROGBITS); - const gchar *result; - - if (!debug_link) - return NULL; - - offset = debug_link->offset; - - result = (char *)(parser->data + offset); - - if (crc32) - { - int len = strlen (result) + 1; - offset = (offset + len + 3) & ~0x3; - - *crc32 = GET_UINT32 (parser, offset); - } - - return result; -} - -const guchar * -get_section (ElfParser *parser, - const char *name) -{ - const Section *section = find_section (parser, name, SHT_PROGBITS); - - if (section) - return parser->data + section->offset; - else - return NULL; -} - -const guchar * -elf_parser_get_eh_frame (ElfParser *parser) -{ - return get_section (parser, ".eh_frame"); -} - -const guchar * -elf_parser_get_debug_frame (ElfParser *parser) -{ - return get_section (parser, ".debug_frame"); -} - -const char * -elf_parser_get_sym_name (ElfParser *parser, - const ElfSym *sym) -{ - g_return_val_if_fail (parser != NULL, NULL); - - return (char *)(parser->data + parser->sym_strings + - st_name (parser, sym->table, sym->offset)); -} - -gboolean -elf_parser_owns_symbol (ElfParser *parser, - const ElfSym *sym) -{ - ElfSym *first, *last; - - if (!parser->n_symbols) - return FALSE; - - first = parser->symbols; - last = parser->symbols + parser->n_symbols - 1; - - return first <= sym && sym <= last; -} - -gulong -elf_parser_get_sym_address (ElfParser *parser, - const ElfSym *sym) -{ - return sym->address - parser->text_section->load_address; -} - -/* - * Utility functions - */ diff --git a/lib/footreedatalist.c b/lib/footreedatalist.c deleted file mode 100644 index 21e48749..00000000 --- a/lib/footreedatalist.c +++ /dev/null @@ -1,577 +0,0 @@ -/* gtktreedatalist.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - * This file contains code shared between GtkTreeStore and GtkListStore. Please - * do not use it. - */ - -#include "footreedatalist.h" -#include - -static FooTreeDataList *cache; - -/* node allocation - */ -#define N_DATA_LISTS (64) - -FooTreeDataList * -_foo_tree_data_list_alloc (void) -{ - FooTreeDataList *list; - - if (!cache) - { - int i; - - list = g_malloc (N_DATA_LISTS * sizeof (FooTreeDataList)); - - for (i = 0; i < N_DATA_LISTS; ++i) - { - list[i].next = cache; - cache = &(list[i]); - } - } - - list = cache; - cache = cache->next; - - memset (list, 0, sizeof (FooTreeDataList)); - - return list; -} - -void -_foo_tree_data_list_free (FooTreeDataList *list, - GType *column_headers) -{ - FooTreeDataList *tmp, *next; - gint i = 0; - - tmp = list; - - while (tmp) - { - next = tmp->next; - if (g_type_is_a (column_headers [i], G_TYPE_STRING)) - g_free ((gchar *) tmp->data.v_pointer); - else if (g_type_is_a (column_headers [i], G_TYPE_OBJECT) && tmp->data.v_pointer != NULL) - g_object_unref (tmp->data.v_pointer); - else if (g_type_is_a (column_headers [i], G_TYPE_BOXED) && tmp->data.v_pointer != NULL) - g_boxed_free (column_headers [i], (gpointer) tmp->data.v_pointer); - - tmp->next = cache; - cache = tmp; - - i++; - tmp = next; - } -} - -gboolean -_foo_tree_data_list_check_type (GType type) -{ - gint i = 0; - static const GType type_list[] = - { - G_TYPE_BOOLEAN, - G_TYPE_CHAR, - G_TYPE_UCHAR, - G_TYPE_INT, - G_TYPE_UINT, - G_TYPE_LONG, - G_TYPE_ULONG, - G_TYPE_INT64, - G_TYPE_UINT64, - G_TYPE_ENUM, - G_TYPE_FLAGS, - G_TYPE_FLOAT, - G_TYPE_DOUBLE, - G_TYPE_STRING, - G_TYPE_POINTER, - G_TYPE_BOXED, - G_TYPE_OBJECT, - G_TYPE_INVALID - }; - - if (! G_TYPE_IS_VALUE_TYPE (type)) - return FALSE; - - - while (type_list[i] != G_TYPE_INVALID) - { - if (g_type_is_a (type, type_list[i])) - return TRUE; - i++; - } - return FALSE; -} - -static inline GType -get_fundamental_type (GType type) -{ - GType result; - - result = G_TYPE_FUNDAMENTAL (type); - - if (result == G_TYPE_INTERFACE) - { - if (g_type_is_a (type, G_TYPE_OBJECT)) - result = G_TYPE_OBJECT; - } - - return result; -} -void -_foo_tree_data_list_node_to_value (FooTreeDataList *list, - GType type, - GValue *value) -{ - g_value_init (value, type); - - switch (get_fundamental_type (type)) - { - case G_TYPE_BOOLEAN: - g_value_set_boolean (value, (gboolean) list->data.v_int); - break; - case G_TYPE_CHAR: - g_value_set_char (value, (gchar) list->data.v_char); - break; - case G_TYPE_UCHAR: - g_value_set_uchar (value, (guchar) list->data.v_uchar); - break; - case G_TYPE_INT: - g_value_set_int (value, (gint) list->data.v_int); - break; - case G_TYPE_UINT: - g_value_set_uint (value, (guint) list->data.v_uint); - break; - case G_TYPE_LONG: - g_value_set_long (value, list->data.v_long); - break; - case G_TYPE_ULONG: - g_value_set_ulong (value, list->data.v_ulong); - break; - case G_TYPE_INT64: - g_value_set_int64 (value, list->data.v_int64); - break; - case G_TYPE_UINT64: - g_value_set_uint64 (value, list->data.v_uint64); - break; - case G_TYPE_ENUM: - g_value_set_enum (value, list->data.v_int); - break; - case G_TYPE_FLAGS: - g_value_set_flags (value, list->data.v_uint); - break; - case G_TYPE_FLOAT: - g_value_set_float (value, (gfloat) list->data.v_float); - break; - case G_TYPE_DOUBLE: - g_value_set_double (value, (gdouble) list->data.v_double); - break; - case G_TYPE_STRING: - g_value_set_string (value, (gchar *) list->data.v_pointer); - break; - case G_TYPE_POINTER: - g_value_set_pointer (value, (gpointer) list->data.v_pointer); - break; - case G_TYPE_BOXED: - g_value_set_boxed (value, (gpointer) list->data.v_pointer); - break; - case G_TYPE_OBJECT: - g_value_set_object (value, (GObject *) list->data.v_pointer); - break; - default: - g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type)); - break; - } -} - -void -_foo_tree_data_list_value_to_node (FooTreeDataList *list, - GValue *value) -{ - switch (get_fundamental_type (G_VALUE_TYPE (value))) - { - case G_TYPE_BOOLEAN: - list->data.v_int = g_value_get_boolean (value); - break; - case G_TYPE_CHAR: - list->data.v_char = g_value_get_char (value); - break; - case G_TYPE_UCHAR: - list->data.v_uchar = g_value_get_uchar (value); - break; - case G_TYPE_INT: - list->data.v_int = g_value_get_int (value); - break; - case G_TYPE_UINT: - list->data.v_uint = g_value_get_uint (value); - break; - case G_TYPE_LONG: - list->data.v_long = g_value_get_long (value); - break; - case G_TYPE_ULONG: - list->data.v_ulong = g_value_get_ulong (value); - break; - case G_TYPE_INT64: - list->data.v_int64 = g_value_get_int64 (value); - break; - case G_TYPE_UINT64: - list->data.v_uint64 = g_value_get_uint64 (value); - break; - case G_TYPE_ENUM: - list->data.v_int = g_value_get_enum (value); - break; - case G_TYPE_FLAGS: - list->data.v_uint = g_value_get_flags (value); - break; - case G_TYPE_POINTER: - list->data.v_pointer = g_value_get_pointer (value); - break; - case G_TYPE_FLOAT: - list->data.v_float = g_value_get_float (value); - break; - case G_TYPE_DOUBLE: - list->data.v_double = g_value_get_double (value); - break; - case G_TYPE_STRING: - g_free (list->data.v_pointer); - list->data.v_pointer = g_value_dup_string (value); - break; - case G_TYPE_OBJECT: - if (list->data.v_pointer) - g_object_unref (list->data.v_pointer); - list->data.v_pointer = g_value_dup_object (value); - break; - case G_TYPE_BOXED: - if (list->data.v_pointer) - g_boxed_free (G_VALUE_TYPE (value), list->data.v_pointer); - list->data.v_pointer = g_value_dup_boxed (value); - break; - default: - g_warning ("%s: Unsupported type (%s) stored.", G_STRLOC, g_type_name (G_VALUE_TYPE (value))); - break; - } -} - -FooTreeDataList * -_foo_tree_data_list_node_copy (FooTreeDataList *list, - GType type) -{ - FooTreeDataList *new_list; - - g_return_val_if_fail (list != NULL, NULL); - - new_list = _foo_tree_data_list_alloc (); - new_list->next = NULL; - - switch (get_fundamental_type (type)) - { - case G_TYPE_BOOLEAN: - case G_TYPE_CHAR: - case G_TYPE_UCHAR: - case G_TYPE_INT: - case G_TYPE_UINT: - case G_TYPE_LONG: - case G_TYPE_ULONG: - case G_TYPE_INT64: - case G_TYPE_UINT64: - case G_TYPE_ENUM: - case G_TYPE_FLAGS: - case G_TYPE_POINTER: - case G_TYPE_FLOAT: - case G_TYPE_DOUBLE: - new_list->data = list->data; - break; - case G_TYPE_STRING: - new_list->data.v_pointer = g_strdup (list->data.v_pointer); - break; - case G_TYPE_OBJECT: - case G_TYPE_INTERFACE: - new_list->data.v_pointer = list->data.v_pointer; - if (new_list->data.v_pointer) - g_object_ref (new_list->data.v_pointer); - break; - case G_TYPE_BOXED: - if (list->data.v_pointer) - new_list->data.v_pointer = g_boxed_copy (type, list->data.v_pointer); - else - new_list->data.v_pointer = NULL; - break; - default: - g_warning ("Unsupported node type (%s) copied.", g_type_name (type)); - break; - } - - return new_list; -} - -gint -_foo_tree_data_list_compare_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - gint column = GPOINTER_TO_INT (user_data); - GType type = gtk_tree_model_get_column_type (model, column); - GValue a_value = {0, }; - GValue b_value = {0, }; - gint retval; - const gchar *stra, *strb; - - gtk_tree_model_get_value (model, a, column, &a_value); - gtk_tree_model_get_value (model, b, column, &b_value); - - switch (get_fundamental_type (type)) - { - case G_TYPE_BOOLEAN: - if (g_value_get_boolean (&a_value) < g_value_get_boolean (&b_value)) - retval = -1; - else if (g_value_get_boolean (&a_value) == g_value_get_boolean (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_CHAR: - if (g_value_get_char (&a_value) < g_value_get_char (&b_value)) - retval = -1; - else if (g_value_get_char (&a_value) == g_value_get_char (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_UCHAR: - if (g_value_get_uchar (&a_value) < g_value_get_uchar (&b_value)) - retval = -1; - else if (g_value_get_uchar (&a_value) == g_value_get_uchar (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_INT: - if (g_value_get_int (&a_value) < g_value_get_int (&b_value)) - retval = -1; - else if (g_value_get_int (&a_value) == g_value_get_int (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_UINT: - if (g_value_get_uint (&a_value) < g_value_get_uint (&b_value)) - retval = -1; - else if (g_value_get_uint (&a_value) == g_value_get_uint (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_LONG: - if (g_value_get_long (&a_value) < g_value_get_long (&b_value)) - retval = -1; - else if (g_value_get_long (&a_value) == g_value_get_long (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_ULONG: - if (g_value_get_ulong (&a_value) < g_value_get_ulong (&b_value)) - retval = -1; - else if (g_value_get_ulong (&a_value) == g_value_get_ulong (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_INT64: - if (g_value_get_int64 (&a_value) < g_value_get_int64 (&b_value)) - retval = -1; - else if (g_value_get_int64 (&a_value) == g_value_get_int64 (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_UINT64: - if (g_value_get_uint64 (&a_value) < g_value_get_uint64 (&b_value)) - retval = -1; - else if (g_value_get_uint64 (&a_value) == g_value_get_uint64 (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_ENUM: - /* this is somewhat bogus. */ - if (g_value_get_enum (&a_value) < g_value_get_enum (&b_value)) - retval = -1; - else if (g_value_get_enum (&a_value) == g_value_get_enum (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_FLAGS: - /* this is even more bogus. */ - if (g_value_get_flags (&a_value) < g_value_get_flags (&b_value)) - retval = -1; - else if (g_value_get_flags (&a_value) == g_value_get_flags (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_FLOAT: - if (g_value_get_float (&a_value) < g_value_get_float (&b_value)) - retval = -1; - else if (g_value_get_float (&a_value) == g_value_get_float (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_DOUBLE: - if (g_value_get_double (&a_value) < g_value_get_double (&b_value)) - retval = -1; - else if (g_value_get_double (&a_value) == g_value_get_double (&b_value)) - retval = 0; - else - retval = 1; - break; - case G_TYPE_STRING: - stra = g_value_get_string (&a_value); - strb = g_value_get_string (&b_value); - if (stra == NULL) stra = ""; - if (strb == NULL) strb = ""; - retval = g_utf8_collate (stra, strb); - break; - case G_TYPE_POINTER: - case G_TYPE_BOXED: - case G_TYPE_OBJECT: - default: - g_warning ("Attempting to sort on invalid type %s\n", g_type_name (type)); - retval = FALSE; - break; - } - - g_value_unset (&a_value); - g_value_unset (&b_value); - - return retval; -} - - -GList * -_foo_tree_data_list_header_new (gint n_columns, - GType *types) -{ - GList *retval = NULL; - - gint i; - - for (i = 0; i < n_columns; i ++) - { - GtkTreeDataSortHeader *header; - - header = g_slice_new (GtkTreeDataSortHeader); - - retval = g_list_prepend (retval, header); - header->sort_column_id = i; - header->func = _foo_tree_data_list_compare_func; - header->destroy = NULL; - header->data = GINT_TO_POINTER (i); - } - return g_list_reverse (retval); -} - -void -_foo_tree_data_list_header_free (GList *list) -{ - GList *tmp; - - for (tmp = list; tmp; tmp = tmp->next) - { - GtkTreeDataSortHeader *header = (GtkTreeDataSortHeader *) tmp->data; - - if (header->destroy) - { - GDestroyNotify d = header->destroy; - - header->destroy = NULL; - d (header->data); - } - - g_slice_free (GtkTreeDataSortHeader, header); - } - g_list_free (list); -} - -GtkTreeDataSortHeader * -_foo_tree_data_list_get_header (GList *header_list, - gint sort_column_id) -{ - GtkTreeDataSortHeader *header = NULL; - - for (; header_list; header_list = header_list->next) - { - header = (GtkTreeDataSortHeader*) header_list->data; - if (header->sort_column_id == sort_column_id) - return header; - } - return NULL; -} - - -GList * -_foo_tree_data_list_set_header (GList *header_list, - gint sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - GList *list = header_list; - GtkTreeDataSortHeader *header = NULL; - - for (; list; list = list->next) - { - header = (GtkTreeDataSortHeader*) list->data; - if (header->sort_column_id == sort_column_id) - break; - header = NULL; - - if (list->next == NULL) - break; - } - - if (header == NULL) - { - header = g_slice_new0 (GtkTreeDataSortHeader); - header->sort_column_id = sort_column_id; - if (list) - list = g_list_append (list, header); - else - header_list = g_list_append (header_list, header); - } - - if (header->destroy) - { - GDestroyNotify d = header->destroy; - - header->destroy = NULL; - d (header->data); - } - - header->func = func; - header->data = data; - header->destroy = destroy; - - return header_list; -} diff --git a/lib/footreedatalist.h b/lib/footreedatalist.h deleted file mode 100644 index eb4274d5..00000000 --- a/lib/footreedatalist.h +++ /dev/null @@ -1,82 +0,0 @@ -/* gtktreedatalist.h - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GTK_TREE_DATA_LIST_H__ -#define __GTK_TREE_DATA_LIST_H__ - -#include - -typedef struct _FooTreeDataList FooTreeDataList; -struct _FooTreeDataList -{ - FooTreeDataList *next; - - union { - gint v_int; - gint8 v_char; - guint8 v_uchar; - guint v_uint; - glong v_long; - gulong v_ulong; - gint64 v_int64; - guint64 v_uint64; - gfloat v_float; - gdouble v_double; - gpointer v_pointer; - } data; -}; - -typedef struct _GtkTreeDataSortHeader -{ - gint sort_column_id; - GtkTreeIterCompareFunc func; - gpointer data; - GDestroyNotify destroy; -} GtkTreeDataSortHeader; - -FooTreeDataList *_foo_tree_data_list_alloc (void); -void _foo_tree_data_list_free (FooTreeDataList *list, - GType *column_headers); -gboolean _foo_tree_data_list_check_type (GType type); -void _foo_tree_data_list_node_to_value (FooTreeDataList *list, - GType type, - GValue *value); -void _foo_tree_data_list_value_to_node (FooTreeDataList *list, - GValue *value); - -FooTreeDataList *_foo_tree_data_list_node_copy (FooTreeDataList *list, - GType type); - -/* Header code */ -gint _foo_tree_data_list_compare_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data); -GList * _foo_tree_data_list_header_new (gint n_columns, - GType *types); -void _foo_tree_data_list_header_free (GList *header_list); -GtkTreeDataSortHeader *_foo_tree_data_list_get_header (GList *header_list, - gint sort_column_id); -GList *_foo_tree_data_list_set_header (GList *header_list, - gint sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); - -#endif /* __GTK_TREE_DATA_LIST_H__ */ diff --git a/lib/footreestore.c b/lib/footreestore.c deleted file mode 100644 index 110f89c5..00000000 --- a/lib/footreestore.c +++ /dev/null @@ -1,3363 +0,0 @@ -/* gtktreestore.c - * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "config.h" -#include -#include -#include -#include "footreedatalist.h" -#include "footreestore.h" - -#define G_NODE(node) ((GNode *)node) -#define FOO_TREE_STORE_IS_SORTED(tree) (((FooTreeStore*)(tree))->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) -#define VALID_ITER(iter, tree_store) ((iter)!= NULL && (iter)->user_data != NULL && ((FooTreeStore*)(tree_store))->stamp == (iter)->stamp) - -/* Get rid of some macros that cause lots of g_atomic_get_pointer() */ -#undef g_return_if_fail -#undef GTK_TREE_MODEL -#undef FOO_TREE_STORE -#define g_return_if_fail(x) -#define GTK_TREE_MODEL(m) ((GtkTreeModel *)m) -#define FOO_TREE_STORE(m) ((FooTreeStore *)m) - -static void foo_tree_store_tree_model_init (GtkTreeModelIface *iface); -static void foo_tree_store_drag_source_init(GtkTreeDragSourceIface *iface); -static void foo_tree_store_drag_dest_init (GtkTreeDragDestIface *iface); -static void foo_tree_store_sortable_init (GtkTreeSortableIface *iface); -static void foo_tree_store_buildable_init (GtkBuildableIface *iface); -static void foo_tree_store_finalize (GObject *object); -static GtkTreeModelFlags foo_tree_store_get_flags (GtkTreeModel *tree_model); -static gint foo_tree_store_get_n_columns (GtkTreeModel *tree_model); -static GType foo_tree_store_get_column_type (GtkTreeModel *tree_model, - gint index); -static gboolean foo_tree_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); -static GtkTreePath *foo_tree_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static void foo_tree_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value); -static gboolean foo_tree_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean foo_tree_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); -static gboolean foo_tree_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gint foo_tree_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean foo_tree_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n); -static gboolean foo_tree_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); - - -static void foo_tree_store_set_n_columns (FooTreeStore *tree_store, - gint n_columns); -static void foo_tree_store_set_column_type (FooTreeStore *tree_store, - gint column, - GType type); - -static void foo_tree_store_increment_stamp (FooTreeStore *tree_store); - - -/* DND interfaces */ -static gboolean real_foo_tree_store_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean foo_tree_store_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean foo_tree_store_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data); -static gboolean foo_tree_store_drag_data_received (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - GtkSelectionData *selection_data); -static gboolean foo_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - GtkSelectionData *selection_data); - -/* Sortable Interfaces */ - -static void foo_tree_store_sort (FooTreeStore *tree_store); -static void foo_tree_store_sort_iter_changed (FooTreeStore *tree_store, - GtkTreeIter *iter, - gint column, - gboolean emit_signal); -static gboolean foo_tree_store_get_sort_column_id (GtkTreeSortable *sortable, - gint *sort_column_id, - GtkSortType *order); -static void foo_tree_store_set_sort_column_id (GtkTreeSortable *sortable, - gint sort_column_id, - GtkSortType order); -static void foo_tree_store_set_sort_func (GtkTreeSortable *sortable, - gint sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static void foo_tree_store_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static gboolean foo_tree_store_has_default_sort_func (GtkTreeSortable *sortable); - - -/* buildable */ - -static gboolean foo_tree_store_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - GMarkupParser *parser, - gpointer *data); -static void foo_tree_store_buildable_custom_finished (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - gpointer user_data); - -static void validate_gnode (GNode *node); - -static void foo_tree_store_move (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position, - gboolean before); - - -static inline void -validate_tree (FooTreeStore *tree_store) -{ - if (gtk_debug_flags & GTK_DEBUG_TREE) - { - g_assert (G_NODE (tree_store->root)->parent == NULL); - - validate_gnode (G_NODE (tree_store->root)); - } -} - -G_DEFINE_TYPE_WITH_CODE (FooTreeStore, foo_tree_store, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - foo_tree_store_tree_model_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - foo_tree_store_drag_source_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, - foo_tree_store_drag_dest_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, - foo_tree_store_sortable_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, - foo_tree_store_buildable_init)) - -static void -foo_tree_store_class_init (FooTreeStoreClass *class) -{ - GObjectClass *object_class; - - object_class = (GObjectClass *) class; - - object_class->finalize = foo_tree_store_finalize; -} - -static void -foo_tree_store_tree_model_init (GtkTreeModelIface *iface) -{ - iface->get_flags = foo_tree_store_get_flags; - iface->get_n_columns = foo_tree_store_get_n_columns; - iface->get_column_type = foo_tree_store_get_column_type; - iface->get_iter = foo_tree_store_get_iter; - iface->get_path = foo_tree_store_get_path; - iface->get_value = foo_tree_store_get_value; - iface->iter_next = foo_tree_store_iter_next; - iface->iter_children = foo_tree_store_iter_children; - iface->iter_has_child = foo_tree_store_iter_has_child; - iface->iter_n_children = foo_tree_store_iter_n_children; - iface->iter_nth_child = foo_tree_store_iter_nth_child; - iface->iter_parent = foo_tree_store_iter_parent; -} - -static void -foo_tree_store_drag_source_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = real_foo_tree_store_row_draggable; - iface->drag_data_delete = foo_tree_store_drag_data_delete; - iface->drag_data_get = foo_tree_store_drag_data_get; -} - -static void -foo_tree_store_drag_dest_init (GtkTreeDragDestIface *iface) -{ - iface->drag_data_received = foo_tree_store_drag_data_received; - iface->row_drop_possible = foo_tree_store_row_drop_possible; -} - -static void -foo_tree_store_sortable_init (GtkTreeSortableIface *iface) -{ - iface->get_sort_column_id = foo_tree_store_get_sort_column_id; - iface->set_sort_column_id = foo_tree_store_set_sort_column_id; - iface->set_sort_func = foo_tree_store_set_sort_func; - iface->set_default_sort_func = foo_tree_store_set_default_sort_func; - iface->has_default_sort_func = foo_tree_store_has_default_sort_func; -} - -void -foo_tree_store_buildable_init (GtkBuildableIface *iface) -{ - iface->custom_tag_start = foo_tree_store_buildable_custom_tag_start; - iface->custom_finished = foo_tree_store_buildable_custom_finished; -} - -static void -foo_tree_store_init (FooTreeStore *tree_store) -{ - tree_store->root = g_node_new (NULL); - /* While the odds are against us getting 0... - */ - do - { - tree_store->stamp = g_random_int (); - } - while (tree_store->stamp == 0); - - tree_store->sort_list = NULL; - tree_store->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; - tree_store->columns_dirty = FALSE; - - tree_store->row_changed_id = g_signal_lookup ("row_changed", GTK_TYPE_TREE_MODEL); - tree_store->row_inserted_id = g_signal_lookup ("row_inserted", GTK_TYPE_TREE_MODEL); - tree_store->row_has_child_toggled_id = g_signal_lookup ("row_has_child_toggled", GTK_TYPE_TREE_MODEL); - tree_store->rows_reordered_id = g_signal_lookup ("rows_reordered", GTK_TYPE_TREE_MODEL); -} - -/** - * foo_tree_store_new: - * @n_columns: number of columns in the tree store - * @Varargs: all #GType types for the columns, from first to last - * - * Creates a new tree store as with @n_columns columns each of the types passed - * in. Note that only types derived from standard GObject fundamental types - * are supported. - * - * As an example, foo_tree_store_new (3, G_TYPE_INT, G_TYPE_STRING, - * GDK_TYPE_PIXBUF); will create a new #FooTreeStore with three columns, of type - * int, string and #GdkPixbuf respectively. - * - * Return value: a new #FooTreeStore - **/ -FooTreeStore * -foo_tree_store_new (gint n_columns, - ...) -{ - FooTreeStore *retval; - va_list args; - gint i; - - g_return_val_if_fail (n_columns > 0, NULL); - - retval = g_object_new (FOO_TYPE_TREE_STORE, NULL); - foo_tree_store_set_n_columns (retval, n_columns); - - va_start (args, n_columns); - - for (i = 0; i < n_columns; i++) - { - GType type = va_arg (args, GType); - if (! _foo_tree_data_list_check_type (type)) - { - g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type)); - g_object_unref (retval); - va_end (args); - return NULL; - } - foo_tree_store_set_column_type (retval, i, type); - } - va_end (args); - - return retval; -} -/** - * foo_tree_store_newv: - * @n_columns: number of columns in the tree store - * @types: an array of #GType types for the columns, from first to last - * - * Non vararg creation function. Used primarily by language bindings. - * - * Return value: a new #FooTreeStore - **/ -FooTreeStore * -foo_tree_store_newv (gint n_columns, - GType *types) -{ - FooTreeStore *retval; - gint i; - - g_return_val_if_fail (n_columns > 0, NULL); - - retval = g_object_new (FOO_TYPE_TREE_STORE, NULL); - foo_tree_store_set_n_columns (retval, n_columns); - - for (i = 0; i < n_columns; i++) - { - if (! _foo_tree_data_list_check_type (types[i])) - { - g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i])); - g_object_unref (retval); - return NULL; - } - foo_tree_store_set_column_type (retval, i, types[i]); - } - - return retval; -} - - -/** - * foo_tree_store_set_column_types: - * @tree_store: A #FooTreeStore - * @n_columns: Number of columns for the tree store - * @types: An array of #GType types, one for each column - * - * This function is meant primarily for #GObjects that inherit from - * #FooTreeStore, and should only be used when constructing a new - * #FooTreeStore. It will not function after a row has been added, - * or a method on the #GtkTreeModel interface is called. - **/ -void -foo_tree_store_set_column_types (FooTreeStore *tree_store, - gint n_columns, - GType *types) -{ - gint i; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (tree_store->columns_dirty == 0); - - foo_tree_store_set_n_columns (tree_store, n_columns); - for (i = 0; i < n_columns; i++) - { - if (! _foo_tree_data_list_check_type (types[i])) - { - g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i])); - continue; - } - foo_tree_store_set_column_type (tree_store, i, types[i]); - } -} - -static void -foo_tree_store_set_n_columns (FooTreeStore *tree_store, - gint n_columns) -{ - GType *new_columns; - - if (tree_store->n_columns == n_columns) - return; - - new_columns = g_new0 (GType, n_columns); - if (tree_store->column_headers) - { - /* copy the old header orders over */ - if (n_columns >= tree_store->n_columns) - memcpy (new_columns, tree_store->column_headers, tree_store->n_columns * sizeof (gchar *)); - else - memcpy (new_columns, tree_store->column_headers, n_columns * sizeof (GType)); - - g_free (tree_store->column_headers); - } - - if (tree_store->sort_list) - _foo_tree_data_list_header_free (tree_store->sort_list); - - tree_store->sort_list = _foo_tree_data_list_header_new (n_columns, tree_store->column_headers); - - tree_store->column_headers = new_columns; - tree_store->n_columns = n_columns; -} - -/** - * foo_tree_store_set_column_type: - * @tree_store: a #FooTreeStore - * @column: column number - * @type: type of the data to be stored in @column - * - * Supported types include: %G_TYPE_UINT, %G_TYPE_INT, %G_TYPE_UCHAR, - * %G_TYPE_CHAR, %G_TYPE_BOOLEAN, %G_TYPE_POINTER, %G_TYPE_FLOAT, - * %G_TYPE_DOUBLE, %G_TYPE_STRING, %G_TYPE_OBJECT, and %G_TYPE_BOXED, along with - * subclasses of those types such as %GDK_TYPE_PIXBUF. - * - **/ -static void -foo_tree_store_set_column_type (FooTreeStore *tree_store, - gint column, - GType type) -{ - if (!_foo_tree_data_list_check_type (type)) - { - g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type)); - return; - } - tree_store->column_headers[column] = type; -} - -static gboolean -node_free (GNode *node, gpointer data) -{ - if (node->data) - _foo_tree_data_list_free (node->data, (GType*)data); - node->data = NULL; - - return FALSE; -} - -static void -foo_tree_store_finalize (GObject *object) -{ - FooTreeStore *tree_store = FOO_TREE_STORE (object); - - g_node_traverse (tree_store->root, G_POST_ORDER, G_TRAVERSE_ALL, -1, - node_free, tree_store->column_headers); - g_node_destroy (tree_store->root); - _foo_tree_data_list_header_free (tree_store->sort_list); - g_free (tree_store->column_headers); - - if (tree_store->default_sort_destroy) - { - GDestroyNotify d = tree_store->default_sort_destroy; - - tree_store->default_sort_destroy = NULL; - d (tree_store->default_sort_data); - tree_store->default_sort_data = NULL; - } - - /* must chain up */ - G_OBJECT_CLASS (foo_tree_store_parent_class)->finalize (object); -} - -/* fulfill the GtkTreeModel requirements */ -/* NOTE: FooTreeStore::root is a GNode, that acts as the parent node. However, - * it is not visible to the tree or to the user., and the path "0" refers to the - * first child of FooTreeStore::root. - */ - - -static GtkTreeModelFlags -foo_tree_store_get_flags (GtkTreeModel *tree_model) -{ - return GTK_TREE_MODEL_ITERS_PERSIST; -} - -static gint -foo_tree_store_get_n_columns (GtkTreeModel *tree_model) -{ - FooTreeStore *tree_store = (FooTreeStore *) tree_model; - - tree_store->columns_dirty = TRUE; - - return tree_store->n_columns; -} - -static GType -foo_tree_store_get_column_type (GtkTreeModel *tree_model, - gint index) -{ - FooTreeStore *tree_store = (FooTreeStore *) tree_model; - - g_return_val_if_fail (index < tree_store->n_columns, G_TYPE_INVALID); - - tree_store->columns_dirty = TRUE; - - return tree_store->column_headers[index]; -} - -static gboolean -foo_tree_store_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - FooTreeStore *tree_store = (FooTreeStore *) tree_model; - GtkTreeIter parent; - gint *indices; - gint depth, i; - - tree_store->columns_dirty = TRUE; - - indices = gtk_tree_path_get_indices (path); - depth = gtk_tree_path_get_depth (path); - - g_return_val_if_fail (depth > 0, FALSE); - - parent.stamp = tree_store->stamp; - parent.user_data = tree_store->root; - - if (!foo_tree_store_iter_nth_child (tree_model, iter, &parent, indices[0])) - return FALSE; - - for (i = 1; i < depth; i++) - { - parent = *iter; - if (!foo_tree_store_iter_nth_child (tree_model, iter, &parent, indices[i])) - return FALSE; - } - - return TRUE; -} - -static GtkTreePath * -foo_tree_store_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - FooTreeStore *tree_store = (FooTreeStore *) tree_model; - GtkTreePath *retval; - GNode *tmp_node; - gint i = 0; - - g_return_val_if_fail (iter->user_data != NULL, NULL); - g_return_val_if_fail (iter->stamp == tree_store->stamp, NULL); - - validate_tree (tree_store); - - if (G_NODE (iter->user_data)->parent == NULL && - G_NODE (iter->user_data) == tree_store->root) - return gtk_tree_path_new (); - g_assert (G_NODE (iter->user_data)->parent != NULL); - - if (G_NODE (iter->user_data)->parent == G_NODE (tree_store->root)) - { - retval = gtk_tree_path_new (); - tmp_node = G_NODE (tree_store->root)->children; - } - else - { - GtkTreeIter tmp_iter = *iter; - - tmp_iter.user_data = G_NODE (iter->user_data)->parent; - - retval = foo_tree_store_get_path (tree_model, &tmp_iter); - tmp_node = G_NODE (iter->user_data)->parent->children; - } - - if (retval == NULL) - return NULL; - - if (tmp_node == NULL) - { - gtk_tree_path_free (retval); - return NULL; - } - - for (; tmp_node; tmp_node = tmp_node->next) - { - if (tmp_node == G_NODE (iter->user_data)) - break; - i++; - } - - if (tmp_node == NULL) - { - /* We couldn't find node, meaning it's prolly not ours */ - /* Perhaps I should do a g_return_if_fail here. */ - gtk_tree_path_free (retval); - return NULL; - } - - gtk_tree_path_append_index (retval, i); - - return retval; -} - - -static void -foo_tree_store_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value) -{ - FooTreeStore *tree_store = (FooTreeStore *) tree_model; - FooTreeDataList *list; - gint tmp_column = column; - - g_return_if_fail (column < tree_store->n_columns); - g_return_if_fail (VALID_ITER (iter, tree_store)); - - list = G_NODE (iter->user_data)->data; - - while (tmp_column-- > 0 && list) - list = list->next; - - if (list) - { - _foo_tree_data_list_node_to_value (list, - tree_store->column_headers[column], - value); - } - else - { - /* We want to return an initialized but empty (default) value */ - g_value_init (value, tree_store->column_headers[column]); - } -} - -static gboolean -foo_tree_store_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail (iter->user_data != NULL, FALSE); - g_return_val_if_fail (iter->stamp == FOO_TREE_STORE (tree_model)->stamp, FALSE); - - if (G_NODE (iter->user_data)->next) - { - iter->user_data = G_NODE (iter->user_data)->next; - return TRUE; - } - else - { - iter->stamp = 0; - return FALSE; - } -} - -static gboolean -foo_tree_store_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - FooTreeStore *tree_store = (FooTreeStore *) tree_model; - GNode *children; - - if (parent) - g_return_val_if_fail (VALID_ITER (parent, tree_store), FALSE); - - if (parent) - children = G_NODE (parent->user_data)->children; - else - children = G_NODE (tree_store->root)->children; - - if (children) - { - iter->stamp = tree_store->stamp; - iter->user_data = children; - return TRUE; - } - else - { - iter->stamp = 0; - return FALSE; - } -} - -static gboolean -foo_tree_store_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail (iter->user_data != NULL, FALSE); - g_return_val_if_fail (VALID_ITER (iter, tree_model), FALSE); - - return G_NODE (iter->user_data)->children != NULL; -} - -static gint -foo_tree_store_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GNode *node; - gint i = 0; - - g_return_val_if_fail (iter == NULL || iter->user_data != NULL, 0); - - if (iter == NULL) - node = G_NODE (FOO_TREE_STORE (tree_model)->root)->children; - else - node = G_NODE (iter->user_data)->children; - - while (node) - { - i++; - node = node->next; - } - - return i; -} - -static gboolean -foo_tree_store_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) -{ - FooTreeStore *tree_store = (FooTreeStore *) tree_model; - GNode *parent_node; - GNode *child; - - g_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE); - - if (parent == NULL) - parent_node = tree_store->root; - else - parent_node = parent->user_data; - - child = g_node_nth_child (parent_node, n); - - if (child) - { - iter->user_data = child; - iter->stamp = tree_store->stamp; - return TRUE; - } - else - { - iter->stamp = 0; - return FALSE; - } -} - -static gboolean -foo_tree_store_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - FooTreeStore *tree_store = (FooTreeStore *) tree_model; - GNode *parent; - - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (VALID_ITER (child, tree_store), FALSE); - - parent = G_NODE (child->user_data)->parent; - - g_assert (parent != NULL); - - if (parent != tree_store->root) - { - iter->user_data = parent; - iter->stamp = tree_store->stamp; - return TRUE; - } - else - { - iter->stamp = 0; - return FALSE; - } -} - - -/* Does not emit a signal */ -static gboolean -foo_tree_store_real_set_value (FooTreeStore *tree_store, - GtkTreeIter *iter, - gint column, - GValue *value, - gboolean sort) -{ - FooTreeDataList *list; - FooTreeDataList *prev; - gint old_column = column; - GValue real_value = {0, }; - gboolean converted = FALSE; - gboolean retval = FALSE; - - if (! g_type_is_a (G_VALUE_TYPE (value), tree_store->column_headers[column])) - { - if (! (g_value_type_compatible (G_VALUE_TYPE (value), tree_store->column_headers[column]) && - g_value_type_compatible (tree_store->column_headers[column], G_VALUE_TYPE (value)))) - { - g_warning ("%s: Unable to convert from %s to %s\n", - G_STRLOC, - g_type_name (G_VALUE_TYPE (value)), - g_type_name (tree_store->column_headers[column])); - return retval; - } - if (!g_value_transform (value, &real_value)) - { - g_warning ("%s: Unable to make conversion from %s to %s\n", - G_STRLOC, - g_type_name (G_VALUE_TYPE (value)), - g_type_name (tree_store->column_headers[column])); - g_value_unset (&real_value); - return retval; - } - converted = TRUE; - } - - prev = list = G_NODE (iter->user_data)->data; - - while (list != NULL) - { - if (column == 0) - { - if (converted) - _foo_tree_data_list_value_to_node (list, &real_value); - else - _foo_tree_data_list_value_to_node (list, value); - retval = TRUE; - if (converted) - g_value_unset (&real_value); - if (sort && FOO_TREE_STORE_IS_SORTED (tree_store)) - foo_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE); - return retval; - } - - column--; - prev = list; - list = list->next; - } - - if (G_NODE (iter->user_data)->data == NULL) - { - G_NODE (iter->user_data)->data = list = _foo_tree_data_list_alloc (); - list->next = NULL; - } - else - { - list = prev->next = _foo_tree_data_list_alloc (); - list->next = NULL; - } - - while (column != 0) - { - list->next = _foo_tree_data_list_alloc (); - list = list->next; - list->next = NULL; - column --; - } - - if (converted) - _foo_tree_data_list_value_to_node (list, &real_value); - else - _foo_tree_data_list_value_to_node (list, value); - - retval = TRUE; - if (converted) - g_value_unset (&real_value); - - if (sort && FOO_TREE_STORE_IS_SORTED (tree_store)) - foo_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE); - - return retval; -} - -/** - * foo_tree_store_set_value: - * @tree_store: a #FooTreeStore - * @iter: A valid #GtkTreeIter for the row being modified - * @column: column number to modify - * @value: new value for the cell - * - * Sets the data in the cell specified by @iter and @column. - * The type of @value must be convertible to the type of the - * column. - * - **/ -void -foo_tree_store_set_value (FooTreeStore *tree_store, - GtkTreeIter *iter, - gint column, - GValue *value) -{ - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (VALID_ITER (iter, tree_store)); - g_return_if_fail (column >= 0 && column < tree_store->n_columns); - g_return_if_fail (G_IS_VALUE (value)); - - if (foo_tree_store_real_set_value (tree_store, iter, column, value, TRUE)) - { - GtkTreePath *path; - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); - gtk_tree_path_free (path); - } -} - -static GtkTreeIterCompareFunc -foo_tree_store_get_compare_func (FooTreeStore *tree_store) -{ - GtkTreeIterCompareFunc func = NULL; - - if (FOO_TREE_STORE_IS_SORTED (tree_store)) - { - if (tree_store->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - header = _foo_tree_data_list_get_header (tree_store->sort_list, - tree_store->sort_column_id); - g_return_val_if_fail (header != NULL, NULL); - g_return_val_if_fail (header->func != NULL, NULL); - func = header->func; - } - else - { - func = tree_store->default_sort_func; - } - } - - return func; -} - -static void -foo_tree_store_set_vector_internal (FooTreeStore *tree_store, - GtkTreeIter *iter, - gboolean *emit_signal, - gboolean *maybe_need_sort, - gint *columns, - GValue *values, - gint n_values) -{ - gint i; - GtkTreeIterCompareFunc func = NULL; - - func = foo_tree_store_get_compare_func (tree_store); - if (func != _foo_tree_data_list_compare_func) - *maybe_need_sort = TRUE; - - for (i = 0; i < n_values; i++) - { - *emit_signal = foo_tree_store_real_set_value (tree_store, iter, - columns[i], &values[i], - FALSE) || *emit_signal; - - if (func == _foo_tree_data_list_compare_func && - columns[i] == tree_store->sort_column_id) - *maybe_need_sort = TRUE; - } -} - -static void -foo_tree_store_set_valist_internal (FooTreeStore *tree_store, - GtkTreeIter *iter, - gboolean *emit_signal, - gboolean *maybe_need_sort, - va_list var_args) -{ - gint column; - GtkTreeIterCompareFunc func = NULL; - - column = va_arg (var_args, gint); - - func = foo_tree_store_get_compare_func (tree_store); - if (func != _foo_tree_data_list_compare_func) - *maybe_need_sort = TRUE; - - while (column != -1) - { - GValue value = { 0, }; - gchar *error = NULL; - - if (column < 0 || column >= tree_store->n_columns) - { - g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column); - break; - } - g_value_init (&value, tree_store->column_headers[column]); - - G_VALUE_COLLECT (&value, var_args, 0, &error); - if (error) - { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - - /* we purposely leak the value here, it might not be - * in a sane state if an error condition occoured - */ - break; - } - - *emit_signal = foo_tree_store_real_set_value (tree_store, - iter, - column, - &value, - FALSE) || *emit_signal; - - if (func == _foo_tree_data_list_compare_func && - column == tree_store->sort_column_id) - *maybe_need_sort = TRUE; - - g_value_unset (&value); - - column = va_arg (var_args, gint); - } -} - -/** - * foo_tree_store_set_valuesv: - * @tree_store: A #FooTreeStore - * @iter: A valid #GtkTreeIter for the row being modified - * @columns: an array of column numbers - * @values: an array of GValues - * @n_values: the length of the @columns and @values arrays - * - * A variant of foo_tree_store_set_valist() which takes - * the columns and values as two arrays, instead of varargs. This - * function is mainly intended for language bindings or in case - * the number of columns to change is not known until run-time. - * - * Since: 2.12 - **/ -void -foo_tree_store_set_valuesv (FooTreeStore *tree_store, - GtkTreeIter *iter, - gint *columns, - GValue *values, - gint n_values) -{ - gboolean emit_signal = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (VALID_ITER (iter, tree_store)); - - foo_tree_store_set_vector_internal (tree_store, iter, - &emit_signal, - &maybe_need_sort, - columns, values, n_values); - - if (maybe_need_sort && FOO_TREE_STORE_IS_SORTED (tree_store)) - foo_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, TRUE); - - if (emit_signal) - { - GtkTreePath *path; - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); - gtk_tree_path_free (path); - } -} - -/** - * foo_tree_store_set_valist: - * @tree_store: A #FooTreeStore - * @iter: A valid #GtkTreeIter for the row being modified - * @var_args: va_list of column/value pairs - * - * See foo_tree_store_set(); this version takes a va_list for - * use by language bindings. - * - **/ -void -foo_tree_store_set_valist (FooTreeStore *tree_store, - GtkTreeIter *iter, - va_list var_args) -{ - gboolean emit_signal = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (VALID_ITER (iter, tree_store)); - - foo_tree_store_set_valist_internal (tree_store, iter, - &emit_signal, - &maybe_need_sort, - var_args); - - if (maybe_need_sort && FOO_TREE_STORE_IS_SORTED (tree_store)) - foo_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, TRUE); - - if (emit_signal) - { - if (g_signal_has_handler_pending (tree_store, tree_store->row_changed_id, 0, FALSE)) - { - GtkTreePath *path; - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter); - gtk_tree_path_free (path); - } - } -} - -/** - * foo_tree_store_set: - * @tree_store: A #FooTreeStore - * @iter: A valid #GtkTreeIter for the row being modified - * @Varargs: pairs of column number and value, terminated with -1 - * - * Sets the value of one or more cells in the row referenced by @iter. - * The variable argument list should contain integer column numbers, - * each column number followed by the value to be set. - * The list is terminated by a -1. For example, to set column 0 with type - * %G_TYPE_STRING to "Foo", you would write - * foo_tree_store_set (store, iter, 0, "Foo", -1). - * The value will be copied or referenced by the store if appropriate. - **/ -void -foo_tree_store_set (FooTreeStore *tree_store, - GtkTreeIter *iter, - ...) -{ - va_list var_args; - - va_start (var_args, iter); - foo_tree_store_set_valist (tree_store, iter, var_args); - va_end (var_args); -} - -/** - * foo_tree_store_remove: - * @tree_store: A #FooTreeStore - * @iter: A valid #GtkTreeIter - * - * Removes @iter from @tree_store. After being removed, @iter is set to the - * next valid row at that level, or invalidated if it previously pointed to the - * last one. - * - * Return value: %TRUE if @iter is still valid, %FALSE if not. - **/ -gboolean -foo_tree_store_remove (FooTreeStore *tree_store, - GtkTreeIter *iter) -{ - GtkTreePath *path; - GtkTreeIter new_iter = {0,}; - GNode *parent; - GNode *next_node; - - g_return_val_if_fail (FOO_IS_TREE_STORE (tree_store), FALSE); - g_return_val_if_fail (VALID_ITER (iter, tree_store), FALSE); - - parent = G_NODE (iter->user_data)->parent; - - g_assert (parent != NULL); - next_node = G_NODE (iter->user_data)->next; - - if (G_NODE (iter->user_data)->data) - g_node_traverse (G_NODE (iter->user_data), G_POST_ORDER, G_TRAVERSE_ALL, - -1, node_free, tree_store->column_headers); - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - g_node_destroy (G_NODE (iter->user_data)); - - gtk_tree_model_row_deleted (GTK_TREE_MODEL (tree_store), path); - - if (parent != G_NODE (tree_store->root)) - { - /* child_toggled */ - if (parent->children == NULL) - { - gtk_tree_path_up (path); - - new_iter.stamp = tree_store->stamp; - new_iter.user_data = parent; - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &new_iter); - } - } - gtk_tree_path_free (path); - - /* revalidate iter */ - if (next_node != NULL) - { - iter->stamp = tree_store->stamp; - iter->user_data = next_node; - return TRUE; - } - else - { - iter->stamp = 0; - iter->user_data = NULL; - } - - return FALSE; -} - -/** - * foo_tree_store_insert: - * @tree_store: A #FooTreeStore - * @iter: An unset #GtkTreeIter to set to the new row - * @parent: A valid #GtkTreeIter, or %NULL - * @position: position to insert the new row - * - * Creates a new row at @position. If parent is non-%NULL, then the row will be - * made a child of @parent. Otherwise, the row will be created at the toplevel. - * If @position is larger than the number of rows at that level, then the new - * row will be inserted to the end of the list. @iter will be changed to point - * to this new row. The row will be empty after this function is called. To - * fill in values, you need to call foo_tree_store_set() or - * foo_tree_store_set_value(). - * - **/ -void -foo_tree_store_insert (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint position) -{ - GNode *parent_node; - GNode *new_node; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - if (parent) - parent_node = parent->user_data; - else - parent_node = tree_store->root; - - tree_store->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - iter->stamp = tree_store->stamp; - iter->user_data = new_node; - g_node_insert (parent_node, position, new_node); - - if (g_signal_has_handler_pending (tree_store, tree_store->row_inserted_id, 0, FALSE) || - g_signal_has_handler_pending (tree_store, tree_store->row_has_child_toggled_id, 0, FALSE)) - { - GtkTreePath *path; - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != tree_store->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - } - - gtk_tree_path_free (path); - } - - validate_tree ((FooTreeStore*)tree_store); -} - -/** - * foo_tree_store_insert_before: - * @tree_store: A #FooTreeStore - * @iter: An unset #GtkTreeIter to set to the new row - * @parent: A valid #GtkTreeIter, or %NULL - * @sibling: A valid #GtkTreeIter, or %NULL - * - * Inserts a new row before @sibling. If @sibling is %NULL, then the row will - * be appended to @parent 's children. If @parent and @sibling are %NULL, then - * the row will be appended to the toplevel. If both @sibling and @parent are - * set, then @parent must be the parent of @sibling. When @sibling is set, - * @parent is optional. - * - * @iter will be changed to point to this new row. The row will be empty after - * this function is called. To fill in values, you need to call - * foo_tree_store_set() or foo_tree_store_set_value(). - * - **/ -void -foo_tree_store_insert_before (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - GtkTreeIter *sibling) -{ - GtkTreePath *path; - GNode *parent_node = NULL; - GNode *new_node; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent != NULL) - g_return_if_fail (VALID_ITER (parent, tree_store)); - if (sibling != NULL) - g_return_if_fail (VALID_ITER (sibling, tree_store)); - - if (parent == NULL && sibling == NULL) - parent_node = tree_store->root; - else if (parent == NULL) - parent_node = G_NODE (sibling->user_data)->parent; - else if (sibling == NULL) - parent_node = G_NODE (parent->user_data); - else - { - g_return_if_fail (G_NODE (sibling->user_data)->parent == G_NODE (parent->user_data)); - parent_node = G_NODE (parent->user_data); - } - - tree_store->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - g_node_insert_before (parent_node, - sibling ? G_NODE (sibling->user_data) : NULL, - new_node); - - iter->stamp = tree_store->stamp; - iter->user_data = new_node; - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != tree_store->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - GtkTreeIter parent_iter; - - parent_iter.stamp = tree_store->stamp; - parent_iter.user_data = parent_node; - - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &parent_iter); - } - } - - gtk_tree_path_free (path); - - validate_tree (tree_store); -} - -/** - * foo_tree_store_insert_after: - * @tree_store: A #FooTreeStore - * @iter: An unset #GtkTreeIter to set to the new row - * @parent: A valid #GtkTreeIter, or %NULL - * @sibling: A valid #GtkTreeIter, or %NULL - * - * Inserts a new row after @sibling. If @sibling is %NULL, then the row will be - * prepended to @parent 's children. If @parent and @sibling are %NULL, then - * the row will be prepended to the toplevel. If both @sibling and @parent are - * set, then @parent must be the parent of @sibling. When @sibling is set, - * @parent is optional. - * - * @iter will be changed to point to this new row. The row will be empty after - * this function is called. To fill in values, you need to call - * foo_tree_store_set() or foo_tree_store_set_value(). - * - **/ -void -foo_tree_store_insert_after (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - GtkTreeIter *sibling) -{ - GtkTreePath *path; - GNode *parent_node; - GNode *new_node; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent != NULL) - g_return_if_fail (VALID_ITER (parent, tree_store)); - if (sibling != NULL) - g_return_if_fail (VALID_ITER (sibling, tree_store)); - - if (parent == NULL && sibling == NULL) - parent_node = tree_store->root; - else if (parent == NULL) - parent_node = G_NODE (sibling->user_data)->parent; - else if (sibling == NULL) - parent_node = G_NODE (parent->user_data); - else - { - g_return_if_fail (G_NODE (sibling->user_data)->parent == - G_NODE (parent->user_data)); - parent_node = G_NODE (parent->user_data); - } - - tree_store->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - g_node_insert_after (parent_node, - sibling ? G_NODE (sibling->user_data) : NULL, - new_node); - - iter->stamp = tree_store->stamp; - iter->user_data = new_node; - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != tree_store->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - GtkTreeIter parent_iter; - - parent_iter.stamp = tree_store->stamp; - parent_iter.user_data = parent_node; - - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &parent_iter); - } - } - - gtk_tree_path_free (path); - - validate_tree (tree_store); -} - -/** - * foo_tree_store_insert_with_values: - * @tree_store: A #FooTreeStore - * @iter: An unset #GtkTreeIter to set the new row, or %NULL. - * @parent: A valid #GtkTreeIter, or %NULL - * @position: position to insert the new row - * @Varargs: pairs of column number and value, terminated with -1 - * - * Creates a new row at @position. @iter will be changed to point to this - * new row. If @position is larger than the number of rows on the list, then - * the new row will be appended to the list. The row will be filled with - * the values given to this function. - * - * Calling - * foo_tree_store_insert_with_values (tree_store, iter, position, ...) - * has the same effect as calling - * |[ - * foo_tree_store_insert (tree_store, iter, position); - * foo_tree_store_set (tree_store, iter, ...); - * ]| - * with the different that the former will only emit a row_inserted signal, - * while the latter will emit row_inserted, row_changed and if the tree store - * is sorted, rows_reordered. Since emitting the rows_reordered signal - * repeatedly can affect the performance of the program, - * foo_tree_store_insert_with_values() should generally be preferred when - * inserting rows in a sorted tree store. - * - * Since: 2.10 - */ -void -foo_tree_store_insert_with_values (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint position, - ...) -{ - GtkTreePath *path; - GNode *parent_node; - GNode *new_node; - GtkTreeIter tmp_iter; - va_list var_args; - gboolean changed = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - - if (!iter) - iter = &tmp_iter; - - if (parent) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - if (parent) - parent_node = parent->user_data; - else - parent_node = tree_store->root; - - tree_store->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - iter->stamp = tree_store->stamp; - iter->user_data = new_node; - g_node_insert (parent_node, position, new_node); - - va_start (var_args, position); - foo_tree_store_set_valist_internal (tree_store, iter, - &changed, &maybe_need_sort, - var_args); - va_end (var_args); - - if (maybe_need_sort && FOO_TREE_STORE_IS_SORTED (tree_store)) - foo_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, FALSE); - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != tree_store->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - } - - gtk_tree_path_free (path); - - validate_tree ((FooTreeStore *)tree_store); -} - -/** - * foo_tree_store_insert_with_valuesv: - * @tree_store: A #FooTreeStore - * @iter: An unset #GtkTreeIter to set the new row, or %NULL. - * @parent: A valid #GtkTreeIter, or %NULL - * @position: position to insert the new row - * @columns: an array of column numbers - * @values: an array of GValues - * @n_values: the length of the @columns and @values arrays - * - * A variant of foo_tree_store_insert_with_values() which takes - * the columns and values as two arrays, instead of varargs. This - * function is mainly intended for language bindings. - * - * Since: 2.10 - */ -void -foo_tree_store_insert_with_valuesv (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint position, - gint *columns, - GValue *values, - gint n_values) -{ - GtkTreePath *path; - GNode *parent_node; - GNode *new_node; - GtkTreeIter tmp_iter; - gboolean changed = FALSE; - gboolean maybe_need_sort = FALSE; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - - if (!iter) - iter = &tmp_iter; - - if (parent) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - if (parent) - parent_node = parent->user_data; - else - parent_node = tree_store->root; - - tree_store->columns_dirty = TRUE; - - new_node = g_node_new (NULL); - - iter->stamp = tree_store->stamp; - iter->user_data = new_node; - g_node_insert (parent_node, position, new_node); - - foo_tree_store_set_vector_internal (tree_store, iter, - &changed, &maybe_need_sort, - columns, values, n_values); - - if (maybe_need_sort && FOO_TREE_STORE_IS_SORTED (tree_store)) - foo_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, FALSE); - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != tree_store->root) - { - if (new_node->prev == NULL && new_node->next == NULL) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - } - - gtk_tree_path_free (path); - - validate_tree ((FooTreeStore *)tree_store); -} - -/** - * foo_tree_store_prepend: - * @tree_store: A #FooTreeStore - * @iter: An unset #GtkTreeIter to set to the prepended row - * @parent: A valid #GtkTreeIter, or %NULL - * - * Prepends a new row to @tree_store. If @parent is non-%NULL, then it will prepend - * the new row before the first child of @parent, otherwise it will prepend a row - * to the top level. @iter will be changed to point to this new row. The row - * will be empty after this function is called. To fill in values, you need to - * call foo_tree_store_set() or foo_tree_store_set_value(). - **/ -void -foo_tree_store_prepend (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GNode *parent_node; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent != NULL) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - tree_store->columns_dirty = TRUE; - - if (parent == NULL) - parent_node = tree_store->root; - else - parent_node = parent->user_data; - - if (parent_node->children == NULL) - { - GtkTreePath *path; - - iter->stamp = tree_store->stamp; - iter->user_data = g_node_new (NULL); - - g_node_prepend (parent_node, G_NODE (iter->user_data)); - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != tree_store->root) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - gtk_tree_path_free (path); - } - else - { - foo_tree_store_insert_after (tree_store, iter, parent, NULL); - } - - validate_tree (tree_store); -} - -/** - * foo_tree_store_append: - * @tree_store: A #FooTreeStore - * @iter: An unset #GtkTreeIter to set to the appended row - * @parent: A valid #GtkTreeIter, or %NULL - * - * Appends a new row to @tree_store. If @parent is non-%NULL, then it will append the - * new row after the last child of @parent, otherwise it will append a row to - * the top level. @iter will be changed to point to this new row. The row will - * be empty after this function is called. To fill in values, you need to call - * foo_tree_store_set() or foo_tree_store_set_value(). - **/ -void -foo_tree_store_append (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - GNode *parent_node; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (iter != NULL); - if (parent != NULL) - g_return_if_fail (VALID_ITER (parent, tree_store)); - - if (parent == NULL) - parent_node = tree_store->root; - else - parent_node = parent->user_data; - - tree_store->columns_dirty = TRUE; - - if (parent_node->children == NULL) - { - GtkTreePath *path; - - iter->stamp = tree_store->stamp; - iter->user_data = g_node_new (NULL); - - g_node_append (parent_node, G_NODE (iter->user_data)); - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter); - - if (parent_node != tree_store->root) - { - gtk_tree_path_up (path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent); - } - gtk_tree_path_free (path); - } - else - { - foo_tree_store_insert_before (tree_store, iter, parent, NULL); - } - - validate_tree (tree_store); -} - -/** - * foo_tree_store_is_ancestor: - * @tree_store: A #FooTreeStore - * @iter: A valid #GtkTreeIter - * @descendant: A valid #GtkTreeIter - * - * Returns %TRUE if @iter is an ancestor of @descendant. That is, @iter is the - * parent (or grandparent or great-grandparent) of @descendant. - * - * Return value: %TRUE, if @iter is an ancestor of @descendant - **/ -gboolean -foo_tree_store_is_ancestor (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *descendant) -{ - g_return_val_if_fail (FOO_IS_TREE_STORE (tree_store), FALSE); - g_return_val_if_fail (VALID_ITER (iter, tree_store), FALSE); - g_return_val_if_fail (VALID_ITER (descendant, tree_store), FALSE); - - return g_node_is_ancestor (G_NODE (iter->user_data), - G_NODE (descendant->user_data)); -} - - -/** - * foo_tree_store_iter_depth: - * @tree_store: A #FooTreeStore - * @iter: A valid #GtkTreeIter - * - * Returns the depth of @iter. This will be 0 for anything on the root level, 1 - * for anything down a level, etc. - * - * Return value: The depth of @iter - **/ -gint -foo_tree_store_iter_depth (FooTreeStore *tree_store, - GtkTreeIter *iter) -{ - g_return_val_if_fail (FOO_IS_TREE_STORE (tree_store), 0); - g_return_val_if_fail (VALID_ITER (iter, tree_store), 0); - - return g_node_depth (G_NODE (iter->user_data)) - 2; -} - -/* simple ripoff from g_node_traverse_post_order */ -static gboolean -foo_tree_store_clear_traverse (GNode *node, - FooTreeStore *store) -{ - GtkTreeIter iter; - - if (node->children) - { - GNode *child; - - child = node->children; - while (child) - { - register GNode *current; - - current = child; - child = current->next; - if (foo_tree_store_clear_traverse (current, store)) - return TRUE; - } - - if (node->parent) - { - iter.stamp = store->stamp; - iter.user_data = node; - - foo_tree_store_remove (store, &iter); - } - } - else if (node->parent) - { - iter.stamp = store->stamp; - iter.user_data = node; - - foo_tree_store_remove (store, &iter); - } - - return FALSE; -} - -static void -foo_tree_store_increment_stamp (FooTreeStore *tree_store) -{ - do - { - tree_store->stamp++; - } - while (tree_store->stamp == 0); -} - -/** - * foo_tree_store_clear: - * @tree_store: a #FooTreeStore - * - * Removes all rows from @tree_store - **/ -void -foo_tree_store_clear (FooTreeStore *tree_store) -{ - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - - foo_tree_store_clear_traverse (tree_store->root, tree_store); - foo_tree_store_increment_stamp (tree_store); -} - -static gboolean -foo_tree_store_iter_is_valid_helper (GtkTreeIter *iter, - GNode *first) -{ - GNode *node; - - node = first; - - do - { - if (node == iter->user_data) - return TRUE; - - if (node->children) - if (foo_tree_store_iter_is_valid_helper (iter, node->children)) - return TRUE; - - node = node->next; - } - while (node); - - return FALSE; -} - -/** - * foo_tree_store_iter_is_valid: - * @tree_store: A #FooTreeStore. - * @iter: A #GtkTreeIter. - * - * WARNING: This function is slow. Only use it for debugging and/or testing - * purposes. - * - * Checks if the given iter is a valid iter for this #FooTreeStore. - * - * Return value: %TRUE if the iter is valid, %FALSE if the iter is invalid. - * - * Since: 2.2 - **/ -gboolean -foo_tree_store_iter_is_valid (FooTreeStore *tree_store, - GtkTreeIter *iter) -{ - g_return_val_if_fail (FOO_IS_TREE_STORE (tree_store), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); - - if (!VALID_ITER (iter, tree_store)) - return FALSE; - - return foo_tree_store_iter_is_valid_helper (iter, tree_store->root); -} - -/* DND */ - - -static gboolean real_foo_tree_store_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - return TRUE; -} - -static gboolean -foo_tree_store_drag_data_delete (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - GtkTreeIter iter; - - if (foo_tree_store_get_iter (GTK_TREE_MODEL (drag_source), - &iter, - path)) - { - foo_tree_store_remove (FOO_TREE_STORE (drag_source), - &iter); - return TRUE; - } - else - { - return FALSE; - } -} - -static gboolean -foo_tree_store_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data) -{ - /* Note that we don't need to handle the GTK_TREE_MODEL_ROW - * target, because the default handler does it for us, but - * we do anyway for the convenience of someone maybe overriding the - * default handler. - */ - - if (gtk_tree_set_row_drag_data (selection_data, - GTK_TREE_MODEL (drag_source), - path)) - { - return TRUE; - } - else - { - /* FIXME handle text targets at least. */ - } - - return FALSE; -} - -static void -copy_node_data (FooTreeStore *tree_store, - GtkTreeIter *src_iter, - GtkTreeIter *dest_iter) -{ - FooTreeDataList *dl = G_NODE (src_iter->user_data)->data; - FooTreeDataList *copy_head = NULL; - FooTreeDataList *copy_prev = NULL; - FooTreeDataList *copy_iter = NULL; - GtkTreePath *path; - gint col; - - col = 0; - while (dl) - { - copy_iter = _foo_tree_data_list_node_copy (dl, tree_store->column_headers[col]); - - if (copy_head == NULL) - copy_head = copy_iter; - - if (copy_prev) - copy_prev->next = copy_iter; - - copy_prev = copy_iter; - - dl = dl->next; - ++col; - } - - G_NODE (dest_iter->user_data)->data = copy_head; - - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), dest_iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, dest_iter); - gtk_tree_path_free (path); -} - -static void -recursive_node_copy (FooTreeStore *tree_store, - GtkTreeIter *src_iter, - GtkTreeIter *dest_iter) -{ - GtkTreeIter child; - GtkTreeModel *model; - - model = GTK_TREE_MODEL (tree_store); - - copy_node_data (tree_store, src_iter, dest_iter); - - if (foo_tree_store_iter_children (model, - &child, - src_iter)) - { - /* Need to create children and recurse. Note our - * dependence on persistent iterators here. - */ - do - { - GtkTreeIter copy; - - /* Gee, a really slow algorithm... ;-) FIXME */ - foo_tree_store_append (tree_store, - ©, - dest_iter); - - recursive_node_copy (tree_store, &child, ©); - } - while (foo_tree_store_iter_next (model, &child)); - } -} - -static gboolean -foo_tree_store_drag_data_received (GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - GtkSelectionData *selection_data) -{ - GtkTreeModel *tree_model; - FooTreeStore *tree_store; - GtkTreeModel *src_model = NULL; - GtkTreePath *src_path = NULL; - gboolean retval = FALSE; - - tree_model = GTK_TREE_MODEL (drag_dest); - tree_store = FOO_TREE_STORE (drag_dest); - - validate_tree (tree_store); - - if (gtk_tree_get_row_drag_data (selection_data, - &src_model, - &src_path) && - src_model == tree_model) - { - /* Copy the given row to a new position */ - GtkTreeIter src_iter; - GtkTreeIter dest_iter; - GtkTreePath *prev; - - if (!foo_tree_store_get_iter (src_model, - &src_iter, - src_path)) - { - goto out; - } - - /* Get the path to insert _after_ (dest is the path to insert _before_) */ - prev = gtk_tree_path_copy (dest); - - if (!gtk_tree_path_prev (prev)) - { - GtkTreeIter dest_parent; - GtkTreePath *parent; - GtkTreeIter *dest_parent_p; - - /* dest was the first spot at the current depth; which means - * we are supposed to prepend. - */ - - /* Get the parent, NULL if parent is the root */ - dest_parent_p = NULL; - parent = gtk_tree_path_copy (dest); - if (gtk_tree_path_up (parent) && - gtk_tree_path_get_depth (parent) > 0) - { - foo_tree_store_get_iter (tree_model, - &dest_parent, - parent); - dest_parent_p = &dest_parent; - } - gtk_tree_path_free (parent); - parent = NULL; - - foo_tree_store_prepend (tree_store, - &dest_iter, - dest_parent_p); - - retval = TRUE; - } - else - { - if (foo_tree_store_get_iter (tree_model, &dest_iter, prev)) - { - GtkTreeIter tmp_iter = dest_iter; - - foo_tree_store_insert_after (tree_store, &dest_iter, NULL, - &tmp_iter); - - retval = TRUE; - } - } - - gtk_tree_path_free (prev); - - /* If we succeeded in creating dest_iter, walk src_iter tree branch, - * duplicating it below dest_iter. - */ - - if (retval) - { - recursive_node_copy (tree_store, - &src_iter, - &dest_iter); - } - } - else - { - /* FIXME maybe add some data targets eventually, or handle text - * targets in the simple case. - */ - - } - - out: - - if (src_path) - gtk_tree_path_free (src_path); - - return retval; -} - -static gboolean -foo_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - GtkSelectionData *selection_data) -{ - GtkTreeModel *src_model = NULL; - GtkTreePath *src_path = NULL; - GtkTreePath *tmp = NULL; - gboolean retval = FALSE; - - /* don't accept drops if the tree has been sorted */ - if (FOO_TREE_STORE_IS_SORTED (drag_dest)) - return FALSE; - - if (!gtk_tree_get_row_drag_data (selection_data, - &src_model, - &src_path)) - goto out; - - /* can only drag to ourselves */ - if (src_model != GTK_TREE_MODEL (drag_dest)) - goto out; - - /* Can't drop into ourself. */ - if (gtk_tree_path_is_ancestor (src_path, - dest_path)) - goto out; - - /* Can't drop if dest_path's parent doesn't exist */ - { - GtkTreeIter iter; - - if (gtk_tree_path_get_depth (dest_path) > 1) - { - tmp = gtk_tree_path_copy (dest_path); - gtk_tree_path_up (tmp); - - if (!foo_tree_store_get_iter (GTK_TREE_MODEL (drag_dest), - &iter, tmp)) - goto out; - } - } - - /* Can otherwise drop anywhere. */ - retval = TRUE; - - out: - - if (src_path) - gtk_tree_path_free (src_path); - if (tmp) - gtk_tree_path_free (tmp); - - return retval; -} - -/* Sorting and reordering */ -typedef struct _SortTuple -{ - gint offset; - GNode *node; -} SortTuple; - -/* Reordering */ -static gint -foo_tree_store_reorder_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - SortTuple *a_reorder; - SortTuple *b_reorder; - - a_reorder = (SortTuple *)a; - b_reorder = (SortTuple *)b; - - if (a_reorder->offset < b_reorder->offset) - return -1; - if (a_reorder->offset > b_reorder->offset) - return 1; - - return 0; -} - -/** - * foo_tree_store_reorder: - * @tree_store: A #FooTreeStore. - * @parent: A #GtkTreeIter. - * @new_order: an array of integers mapping the new position of each child - * to its old position before the re-ordering, - * i.e. @new_order[newpos] = oldpos. - * - * Reorders the children of @parent in @tree_store to follow the order - * indicated by @new_order. Note that this function only works with - * unsorted stores. - * - * Since: 2.2 - **/ -void -foo_tree_store_reorder (FooTreeStore *tree_store, - GtkTreeIter *parent, - gint *new_order) -{ - gint i, length = 0; - GNode *level, *node; - GtkTreePath *path; - SortTuple *sort_array; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (!FOO_TREE_STORE_IS_SORTED (tree_store)); - g_return_if_fail (parent == NULL || VALID_ITER (parent, tree_store)); - g_return_if_fail (new_order != NULL); - - if (!parent) - level = G_NODE (tree_store->root)->children; - else - level = G_NODE (parent->user_data)->children; - - /* count nodes */ - node = level; - while (node) - { - length++; - node = node->next; - } - - /* set up sortarray */ - sort_array = g_new (SortTuple, length); - - node = level; - for (i = 0; i < length; i++) - { - sort_array[new_order[i]].offset = i; - sort_array[i].node = node; - - node = node->next; - } - - g_qsort_with_data (sort_array, - length, - sizeof (SortTuple), - foo_tree_store_reorder_func, - NULL); - - /* fix up level */ - for (i = 0; i < length - 1; i++) - { - sort_array[i].node->next = sort_array[i+1].node; - sort_array[i+1].node->prev = sort_array[i].node; - } - - sort_array[length-1].node->next = NULL; - sort_array[0].node->prev = NULL; - if (parent) - G_NODE (parent->user_data)->children = sort_array[0].node; - else - G_NODE (tree_store->root)->children = sort_array[0].node; - - /* emit signal */ - if (parent) - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), parent); - else - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), path, - parent, new_order); - gtk_tree_path_free (path); - g_free (sort_array); -} - -/** - * foo_tree_store_swap: - * @tree_store: A #FooTreeStore. - * @a: A #GtkTreeIter. - * @b: Another #GtkTreeIter. - * - * Swaps @a and @b in the same level of @tree_store. Note that this function - * only works with unsorted stores. - * - * Since: 2.2 - **/ -void -foo_tree_store_swap (FooTreeStore *tree_store, - GtkTreeIter *a, - GtkTreeIter *b) -{ - GNode *tmp, *node_a, *node_b, *parent_node; - GNode *a_prev, *a_next, *b_prev, *b_next; - gint i, a_count, b_count, length, *order; - GtkTreePath *path_a, *path_b; - GtkTreeIter parent; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (VALID_ITER (a, tree_store)); - g_return_if_fail (VALID_ITER (b, tree_store)); - - node_a = G_NODE (a->user_data); - node_b = G_NODE (b->user_data); - - /* basic sanity checking */ - if (node_a == node_b) - return; - - path_a = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), a); - path_b = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), b); - - g_return_if_fail (path_a && path_b); - - gtk_tree_path_up (path_a); - gtk_tree_path_up (path_b); - - if (gtk_tree_path_get_depth (path_a) == 0 - || gtk_tree_path_get_depth (path_b) == 0) - { - if (gtk_tree_path_get_depth (path_a) != gtk_tree_path_get_depth (path_b)) - { - gtk_tree_path_free (path_a); - gtk_tree_path_free (path_b); - - g_warning ("Given children are not in the same level\n"); - return; - } - parent_node = G_NODE (tree_store->root); - } - else - { - if (gtk_tree_path_compare (path_a, path_b)) - { - gtk_tree_path_free (path_a); - gtk_tree_path_free (path_b); - - g_warning ("Given children don't have a common parent\n"); - return; - } - foo_tree_store_get_iter (GTK_TREE_MODEL (tree_store), &parent, - path_a); - parent_node = G_NODE (parent.user_data); - } - gtk_tree_path_free (path_b); - - /* old links which we have to keep around */ - a_prev = node_a->prev; - a_next = node_a->next; - - b_prev = node_b->prev; - b_next = node_b->next; - - /* fix up links if the nodes are next to eachother */ - if (a_prev == node_b) - a_prev = node_a; - if (a_next == node_b) - a_next = node_a; - - if (b_prev == node_a) - b_prev = node_b; - if (b_next == node_a) - b_next = node_b; - - /* counting nodes */ - tmp = parent_node->children; - i = a_count = b_count = 0; - while (tmp) - { - if (tmp == node_a) - a_count = i; - if (tmp == node_b) - b_count = i; - - tmp = tmp->next; - i++; - } - length = i; - - /* hacking the tree */ - if (!a_prev) - parent_node->children = node_b; - else - a_prev->next = node_b; - - if (a_next) - a_next->prev = node_b; - - if (!b_prev) - parent_node->children = node_a; - else - b_prev->next = node_a; - - if (b_next) - b_next->prev = node_a; - - node_a->prev = b_prev; - node_a->next = b_next; - - node_b->prev = a_prev; - node_b->next = a_next; - - /* emit signal */ - order = g_new (gint, length); - for (i = 0; i < length; i++) - if (i == a_count) - order[i] = b_count; - else if (i == b_count) - order[i] = a_count; - else - order[i] = i; - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), path_a, - parent_node == tree_store->root - ? NULL : &parent, order); - gtk_tree_path_free (path_a); - g_free (order); -} - -/* WARNING: this function is *incredibly* fragile. Please smashtest after - * making changes here. - * -Kris - */ -static void -foo_tree_store_move (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position, - gboolean before) -{ - GNode *parent, *node, *a, *b, *tmp, *tmp_a, *tmp_b; - gint old_pos, new_pos, length, i, *order; - GtkTreePath *path = NULL, *tmppath, *pos_path = NULL; - GtkTreeIter parent_iter, dst_a, dst_b; - gint depth = 0; - gboolean handle_b = TRUE; - - g_return_if_fail (FOO_IS_TREE_STORE (tree_store)); - g_return_if_fail (!FOO_TREE_STORE_IS_SORTED (tree_store)); - g_return_if_fail (VALID_ITER (iter, tree_store)); - if (position) - g_return_if_fail (VALID_ITER (position, tree_store)); - - a = b = NULL; - - /* sanity checks */ - if (position) - { - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - pos_path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), - position); - - /* if before: - * moving the iter before path or "path + 1" doesn't make sense - * else - * moving the iter before path or "path - 1" doesn't make sense - */ - if (!gtk_tree_path_compare (path, pos_path)) - goto free_paths_and_out; - - if (before) - gtk_tree_path_next (path); - else - gtk_tree_path_prev (path); - - if (!gtk_tree_path_compare (path, pos_path)) - goto free_paths_and_out; - - if (before) - gtk_tree_path_prev (path); - else - gtk_tree_path_next (path); - - if (gtk_tree_path_get_depth (path) != gtk_tree_path_get_depth (pos_path)) - { - g_warning ("Given children are not in the same level\n"); - - goto free_paths_and_out; - } - - tmppath = gtk_tree_path_copy (pos_path); - gtk_tree_path_up (path); - gtk_tree_path_up (tmppath); - - if (gtk_tree_path_get_depth (path) > 0 && - gtk_tree_path_compare (path, tmppath)) - { - g_warning ("Given children are not in the same level\n"); - - gtk_tree_path_free (tmppath); - goto free_paths_and_out; - } - - gtk_tree_path_free (tmppath); - } - - if (!path) - { - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter); - gtk_tree_path_up (path); - } - - depth = gtk_tree_path_get_depth (path); - - if (depth) - { - foo_tree_store_get_iter (GTK_TREE_MODEL (tree_store), - &parent_iter, path); - - parent = G_NODE (parent_iter.user_data); - } - else - parent = G_NODE (tree_store->root); - - /* yes, I know that this can be done shorter, but I'm doing it this way - * so the code is also maintainable - */ - - if (before && position) - { - b = G_NODE (position->user_data); - - if (gtk_tree_path_get_indices (pos_path)[gtk_tree_path_get_depth (pos_path) - 1] > 0) - { - gtk_tree_path_prev (pos_path); - if (foo_tree_store_get_iter (GTK_TREE_MODEL (tree_store), - &dst_a, pos_path)) - a = G_NODE (dst_a.user_data); - else - a = NULL; - gtk_tree_path_next (pos_path); - } - - /* if b is NULL, a is NULL too -- we are at the beginning of the list - * yes and we leak memory here ... - */ - g_return_if_fail (b); - } - else if (before && !position) - { - /* move before without position is appending */ - a = NULL; - b = NULL; - } - else /* !before */ - { - if (position) - a = G_NODE (position->user_data); - else - a = NULL; - - if (position) - { - gtk_tree_path_next (pos_path); - if (foo_tree_store_get_iter (GTK_TREE_MODEL (tree_store), &dst_b, pos_path)) - b = G_NODE (dst_b.user_data); - else - b = NULL; - gtk_tree_path_prev (pos_path); - } - else - { - /* move after without position is prepending */ - if (depth) - foo_tree_store_iter_children (GTK_TREE_MODEL (tree_store), &dst_b, - &parent_iter); - else - foo_tree_store_iter_children (GTK_TREE_MODEL (tree_store), &dst_b, - NULL); - - b = G_NODE (dst_b.user_data); - } - - /* if a is NULL, b is NULL too -- we are at the end of the list - * yes and we leak memory here ... - */ - if (position) - g_return_if_fail (a); - } - - /* counting nodes */ - tmp = parent->children; - - length = old_pos = 0; - while (tmp) - { - if (tmp == iter->user_data) - old_pos = length; - - tmp = tmp->next; - length++; - } - - /* remove node from list */ - node = G_NODE (iter->user_data); - tmp_a = node->prev; - tmp_b = node->next; - - if (tmp_a) - tmp_a->next = tmp_b; - else - parent->children = tmp_b; - - if (tmp_b) - tmp_b->prev = tmp_a; - - /* and reinsert the node */ - if (a) - { - tmp = a->next; - - a->next = node; - node->next = tmp; - node->prev = a; - } - else if (!a && !before) - { - tmp = parent->children; - - node->prev = NULL; - parent->children = node; - - node->next = tmp; - if (tmp) - tmp->prev = node; - - handle_b = FALSE; - } - else if (!a && before) - { - if (!position) - { - node->parent = NULL; - node->next = node->prev = NULL; - - /* before with sibling = NULL appends */ - g_node_insert_before (parent, NULL, node); - } - else - { - node->parent = NULL; - node->next = node->prev = NULL; - - /* after with sibling = NULL prepends */ - g_node_insert_after (parent, NULL, node); - } - - handle_b = FALSE; - } - - if (handle_b) - { - if (b) - { - tmp = b->prev; - - b->prev = node; - node->prev = tmp; - node->next = b; - } - else if (!(!a && before)) /* !a && before is completely handled above */ - node->next = NULL; - } - - /* emit signal */ - if (position) - new_pos = gtk_tree_path_get_indices (pos_path)[gtk_tree_path_get_depth (pos_path)-1]; - else if (before) - { - if (depth) - new_pos = foo_tree_store_iter_n_children (GTK_TREE_MODEL (tree_store), - &parent_iter) - 1; - else - new_pos = foo_tree_store_iter_n_children (GTK_TREE_MODEL (tree_store), - NULL) - 1; - } - else - new_pos = 0; - - if (new_pos > old_pos) - { - if (before && position) - new_pos--; - } - else - { - if (!before && position) - new_pos++; - } - - order = g_new (gint, length); - if (new_pos > old_pos) - { - for (i = 0; i < length; i++) - if (i < old_pos) - order[i] = i; - else if (i >= old_pos && i < new_pos) - order[i] = i + 1; - else if (i == new_pos) - order[i] = old_pos; - else - order[i] = i; - } - else - { - for (i = 0; i < length; i++) - if (i == new_pos) - order[i] = old_pos; - else if (i > new_pos && i <= old_pos) - order[i] = i - 1; - else - order[i] = i; - } - - if (depth) - { - tmppath = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), - &parent_iter); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), - tmppath, &parent_iter, order); - } - else - { - tmppath = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), - tmppath, NULL, order); - } - - gtk_tree_path_free (tmppath); - gtk_tree_path_free (path); - if (position) - gtk_tree_path_free (pos_path); - g_free (order); - - return; - -free_paths_and_out: - gtk_tree_path_free (path); - gtk_tree_path_free (pos_path); -} - -/** - * foo_tree_store_move_before: - * @tree_store: A #FooTreeStore. - * @iter: A #GtkTreeIter. - * @position: A #GtkTreeIter or %NULL. - * - * Moves @iter in @tree_store to the position before @position. @iter and - * @position should be in the same level. Note that this function only - * works with unsorted stores. If @position is %NULL, @iter will be - * moved to the end of the level. - * - * Since: 2.2 - **/ -void -foo_tree_store_move_before (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position) -{ - foo_tree_store_move (tree_store, iter, position, TRUE); -} - -/** - * foo_tree_store_move_after: - * @tree_store: A #FooTreeStore. - * @iter: A #GtkTreeIter. - * @position: A #GtkTreeIter. - * - * Moves @iter in @tree_store to the position after @position. @iter and - * @position should be in the same level. Note that this function only - * works with unsorted stores. If @position is %NULL, @iter will be moved - * to the start of the level. - * - * Since: 2.2 - **/ -void -foo_tree_store_move_after (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position) -{ - foo_tree_store_move (tree_store, iter, position, FALSE); -} - -/* Sorting */ -static gint -foo_tree_store_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - FooTreeStore *tree_store = user_data; - GNode *node_a; - GNode *node_b; - GtkTreeIterCompareFunc func; - gpointer data; - - GtkTreeIter iter_a; - GtkTreeIter iter_b; - gint retval; - - if (tree_store->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - - header = _foo_tree_data_list_get_header (tree_store->sort_list, - tree_store->sort_column_id); - g_return_val_if_fail (header != NULL, 0); - g_return_val_if_fail (header->func != NULL, 0); - - func = header->func; - data = header->data; - } - else - { - g_return_val_if_fail (tree_store->default_sort_func != NULL, 0); - func = tree_store->default_sort_func; - data = tree_store->default_sort_data; - } - - node_a = ((SortTuple *) a)->node; - node_b = ((SortTuple *) b)->node; - - iter_a.stamp = tree_store->stamp; - iter_a.user_data = node_a; - iter_b.stamp = tree_store->stamp; - iter_b.user_data = node_b; - - retval = (* func) (GTK_TREE_MODEL (user_data), &iter_a, &iter_b, data); - - if (tree_store->order == GTK_SORT_DESCENDING) - { - if (retval > 0) - retval = -1; - else if (retval < 0) - retval = 1; - } - return retval; -} - -static void -foo_tree_store_sort_helper (FooTreeStore *tree_store, - GNode *parent, - gboolean recurse) -{ - GtkTreeIter iter; - GArray *sort_array; - GNode *node; - GNode *tmp_node; - gint list_length; - gint i; - gint *new_order; - GtkTreePath *path; - - node = parent->children; - if (node == NULL || node->next == NULL) - { - if (recurse && node && node->children) - foo_tree_store_sort_helper (tree_store, node, TRUE); - - return; - } - - list_length = 0; - for (tmp_node = node; tmp_node; tmp_node = tmp_node->next) - list_length++; - - sort_array = g_array_sized_new (FALSE, FALSE, sizeof (SortTuple), list_length); - - i = 0; - for (tmp_node = node; tmp_node; tmp_node = tmp_node->next) - { - SortTuple tuple; - - tuple.offset = i; - tuple.node = tmp_node; - g_array_append_val (sort_array, tuple); - i++; - } - - /* Sort the array */ - g_array_sort_with_data (sort_array, foo_tree_store_compare_func, tree_store); - - for (i = 0; i < list_length - 1; i++) - { - g_array_index (sort_array, SortTuple, i).node->next = - g_array_index (sort_array, SortTuple, i + 1).node; - g_array_index (sort_array, SortTuple, i + 1).node->prev = - g_array_index (sort_array, SortTuple, i).node; - } - g_array_index (sort_array, SortTuple, list_length - 1).node->next = NULL; - g_array_index (sort_array, SortTuple, 0).node->prev = NULL; - parent->children = g_array_index (sort_array, SortTuple, 0).node; - - /* Let the world know about our new order */ - new_order = g_new (gint, list_length); - for (i = 0; i < list_length; i++) - new_order[i] = g_array_index (sort_array, SortTuple, i).offset; - - iter.stamp = tree_store->stamp; - iter.user_data = parent; - - if (g_signal_has_handler_pending (tree_store, tree_store->row_inserted_id, 0, FALSE)) - { - path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), &iter); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), - path, &iter, new_order); - gtk_tree_path_free (path); - } - - g_free (new_order); - g_array_free (sort_array, TRUE); - - if (recurse) - { - for (tmp_node = parent->children; tmp_node; tmp_node = tmp_node->next) - { - if (tmp_node->children) - foo_tree_store_sort_helper (tree_store, tmp_node, TRUE); - } - } -} - -static void -foo_tree_store_sort (FooTreeStore *tree_store) -{ - if (!FOO_TREE_STORE_IS_SORTED (tree_store)) - return; - - if (tree_store->sort_column_id != -1) - { - GtkTreeDataSortHeader *header = NULL; - - header = _foo_tree_data_list_get_header (tree_store->sort_list, - tree_store->sort_column_id); - - /* We want to make sure that we have a function */ - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - } - else - { - g_return_if_fail (tree_store->default_sort_func != NULL); - } - - foo_tree_store_sort_helper (tree_store, G_NODE (tree_store->root), TRUE); -} - -static void -foo_tree_store_sort_iter_changed (FooTreeStore *tree_store, - GtkTreeIter *iter, - gint column, - gboolean emit_signal) -{ - GNode *prev = NULL; - GNode *next = NULL; - GNode *node; - GtkTreePath *tmp_path; - GtkTreeIter tmp_iter; - gint cmp_a = 0; - gint cmp_b = 0; - gint i; - gint old_location; - gint new_location; - gint *new_order; - gint length; - GtkTreeIterCompareFunc func; - gpointer data; - - g_return_if_fail (G_NODE (iter->user_data)->parent != NULL); - - tmp_iter.stamp = tree_store->stamp; - if (tree_store->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - header = _foo_tree_data_list_get_header (tree_store->sort_list, - tree_store->sort_column_id); - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - func = header->func; - data = header->data; - } - else - { - g_return_if_fail (tree_store->default_sort_func != NULL); - func = tree_store->default_sort_func; - data = tree_store->default_sort_data; - } - - /* If it's the built in function, we don't sort. */ - if (func == _foo_tree_data_list_compare_func && - tree_store->sort_column_id != column) - return; - - old_location = 0; - node = G_NODE (iter->user_data)->parent->children; - /* First we find the iter, its prev, and its next */ - while (node) - { - if (node == G_NODE (iter->user_data)) - break; - old_location++; - node = node->next; - } - g_assert (node != NULL); - - prev = node->prev; - next = node->next; - - /* Check the common case, where we don't need to sort it moved. */ - if (prev != NULL) - { - tmp_iter.user_data = prev; - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); - } - - if (next != NULL) - { - tmp_iter.user_data = next; - cmp_b = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); - } - - if (tree_store->order == GTK_SORT_DESCENDING) - { - if (cmp_a < 0) - cmp_a = 1; - else if (cmp_a > 0) - cmp_a = -1; - - if (cmp_b < 0) - cmp_b = 1; - else if (cmp_b > 0) - cmp_b = -1; - } - - if (prev == NULL && cmp_b <= 0) - return; - else if (next == NULL && cmp_a <= 0) - return; - else if (prev != NULL && next != NULL && - cmp_a <= 0 && cmp_b <= 0) - return; - - /* We actually need to sort it */ - /* First, remove the old link. */ - - if (prev) - prev->next = next; - else - node->parent->children = next; - - if (next) - next->prev = prev; - - node->prev = NULL; - node->next = NULL; - - /* FIXME: as an optimization, we can potentially start at next */ - prev = NULL; - node = node->parent->children; - new_location = 0; - tmp_iter.user_data = node; - if (tree_store->order == GTK_SORT_DESCENDING) - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); - else - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); - - while ((node->next) && (cmp_a > 0)) - { - prev = node; - node = node->next; - new_location++; - tmp_iter.user_data = node; - if (tree_store->order == GTK_SORT_DESCENDING) - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data); - else - cmp_a = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data); - } - - if ((!node->next) && (cmp_a > 0)) - { - new_location++; - node->next = G_NODE (iter->user_data); - node->next->prev = node; - } - else if (prev) - { - prev->next = G_NODE (iter->user_data); - prev->next->prev = prev; - G_NODE (iter->user_data)->next = node; - G_NODE (iter->user_data)->next->prev = G_NODE (iter->user_data); - } - else - { - G_NODE (iter->user_data)->next = G_NODE (iter->user_data)->parent->children; - G_NODE (iter->user_data)->next->prev = G_NODE (iter->user_data); - G_NODE (iter->user_data)->parent->children = G_NODE (iter->user_data); - } - - if (!emit_signal) - return; - - /* Emit the reordered signal. */ - length = g_node_n_children (node->parent); - new_order = g_new (int, length); - if (old_location < new_location) - for (i = 0; i < length; i++) - { - if (i < old_location || - i > new_location) - new_order[i] = i; - else if (i >= old_location && - i < new_location) - new_order[i] = i + 1; - else if (i == new_location) - new_order[i] = old_location; - } - else - for (i = 0; i < length; i++) - { - if (i < new_location || - i > old_location) - new_order[i] = i; - else if (i > new_location && - i <= old_location) - new_order[i] = i - 1; - else if (i == new_location) - new_order[i] = old_location; - } - - tmp_iter.user_data = node->parent; - tmp_path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), &tmp_iter); - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), - tmp_path, &tmp_iter, - new_order); - - gtk_tree_path_free (tmp_path); - g_free (new_order); -} - - -static gboolean -foo_tree_store_get_sort_column_id (GtkTreeSortable *sortable, - gint *sort_column_id, - GtkSortType *order) -{ - FooTreeStore *tree_store = (FooTreeStore *) sortable; - - if (sort_column_id) - * sort_column_id = tree_store->sort_column_id; - if (order) - * order = tree_store->order; - - if (tree_store->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || - tree_store->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - return FALSE; - - return TRUE; -} - -static void -foo_tree_store_set_sort_column_id (GtkTreeSortable *sortable, - gint sort_column_id, - GtkSortType order) -{ - FooTreeStore *tree_store = (FooTreeStore *) sortable; - - - if ((tree_store->sort_column_id == sort_column_id) && - (tree_store->order == order)) - return; - - if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) - { - if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - { - GtkTreeDataSortHeader *header = NULL; - - header = _foo_tree_data_list_get_header (tree_store->sort_list, - sort_column_id); - - /* We want to make sure that we have a function */ - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - } - else - { - g_return_if_fail (tree_store->default_sort_func != NULL); - } - } - - tree_store->sort_column_id = sort_column_id; - tree_store->order = order; - - gtk_tree_sortable_sort_column_changed (sortable); - - foo_tree_store_sort (tree_store); -} - -static void -foo_tree_store_set_sort_func (GtkTreeSortable *sortable, - gint sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - FooTreeStore *tree_store = (FooTreeStore *) sortable; - - tree_store->sort_list = _foo_tree_data_list_set_header (tree_store->sort_list, - sort_column_id, - func, data, destroy); - - if (tree_store->sort_column_id == sort_column_id) - foo_tree_store_sort (tree_store); -} - -static void -foo_tree_store_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy) -{ - FooTreeStore *tree_store = (FooTreeStore *) sortable; - - if (tree_store->default_sort_destroy) - { - GDestroyNotify d = tree_store->default_sort_destroy; - - tree_store->default_sort_destroy = NULL; - d (tree_store->default_sort_data); - } - - tree_store->default_sort_func = func; - tree_store->default_sort_data = data; - tree_store->default_sort_destroy = destroy; - - if (tree_store->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - foo_tree_store_sort (tree_store); -} - -static gboolean -foo_tree_store_has_default_sort_func (GtkTreeSortable *sortable) -{ - FooTreeStore *tree_store = (FooTreeStore *) sortable; - - return (tree_store->default_sort_func != NULL); -} - -static void -validate_gnode (GNode* node) -{ - GNode *iter; - - iter = node->children; - while (iter != NULL) - { - g_assert (iter->parent == node); - if (iter->prev) - g_assert (iter->prev->next == iter); - validate_gnode (iter); - iter = iter->next; - } -} - -/* GtkBuildable custom tag implementation - * - * - * - * - * - */ -typedef struct { - GtkBuilder *builder; - GObject *object; - GSList *items; -} GSListSubParserData; - -static void -tree_model_start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **names, - const gchar **values, - gpointer user_data, - GError **error) -{ - guint i; - GSListSubParserData *data = (GSListSubParserData*)user_data; - - for (i = 0; names[i]; i++) - { - if (strcmp (names[i], "type") == 0) - data->items = g_slist_prepend (data->items, g_strdup (values[i])); - } -} - -static void -tree_model_end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) -{ - GSListSubParserData *data = (GSListSubParserData*)user_data; - - g_assert(data->builder); - - if (strcmp (element_name, "columns") == 0) - { - GSList *l; - GType *types; - int i; - GType type; - - data = (GSListSubParserData*)user_data; - data->items = g_slist_reverse (data->items); - types = g_new0 (GType, g_slist_length (data->items)); - - for (l = data->items, i = 0; l; l = l->next, i++) - { - type = gtk_builder_get_type_from_name (data->builder, l->data); - if (type == G_TYPE_INVALID) - { - g_warning ("Unknown type %s specified in treemodel %s", - (const gchar*)l->data, - gtk_buildable_get_name (GTK_BUILDABLE (data->object))); - continue; - } - types[i] = type; - - g_free (l->data); - } - - foo_tree_store_set_column_types (FOO_TREE_STORE (data->object), i, types); - - g_free (types); - } - else if (strcmp (element_name, "column") == 0) - ; -} - -static const GMarkupParser tree_model_parser = - { - tree_model_start_element, - tree_model_end_element - }; - - -static gboolean -foo_tree_store_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - GMarkupParser *parser, - gpointer *data) -{ - GSListSubParserData *parser_data; - - if (child) - return FALSE; - - if (strcmp (tagname, "columns") == 0) - { - parser_data = g_slice_new0 (GSListSubParserData); - parser_data->builder = builder; - parser_data->items = NULL; - parser_data->object = G_OBJECT (buildable); - - *parser = tree_model_parser; - *data = parser_data; - return TRUE; - } - - return FALSE; -} - -static void -foo_tree_store_buildable_custom_finished (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - gpointer user_data) -{ - GSListSubParserData *data; - - if (strcmp (tagname, "columns")) - return; - - data = (GSListSubParserData*)user_data; - - g_slist_free (data->items); - g_slice_free (GSListSubParserData, data); -} diff --git a/lib/footreestore.h b/lib/footreestore.h deleted file mode 100644 index 04b3d8e7..00000000 --- a/lib/footreestore.h +++ /dev/null @@ -1,158 +0,0 @@ -/* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __FOO_TREE_STORE_H__ -#define __FOO_TREE_STORE_H__ - -#include -#include - - -G_BEGIN_DECLS - - -#define FOO_TYPE_TREE_STORE (foo_tree_store_get_type ()) -#define FOO_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_TREE_STORE, FooTreeStore)) -#define FOO_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_TREE_STORE, FooTreeStoreClass)) -#define FOO_IS_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_TREE_STORE)) -#define FOO_IS_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_TREE_STORE)) -#define FOO_TREE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_TREE_STORE, FooTreeStoreClass)) - -typedef struct _FooTreeStore FooTreeStore; -typedef struct _FooTreeStoreClass FooTreeStoreClass; - -struct _FooTreeStore -{ - GObject parent; - - gint stamp; - gpointer root; - gpointer last; - gint n_columns; - gint sort_column_id; - GList *sort_list; - GtkSortType order; - GType *column_headers; - GtkTreeIterCompareFunc default_sort_func; - gpointer default_sort_data; - GDestroyNotify default_sort_destroy; - - guint row_changed_id; - guint row_inserted_id; - guint row_has_child_toggled_id; - guint rows_reordered_id; - - guint (columns_dirty) : 1; -}; - -struct _FooTreeStoreClass -{ - GObjectClass parent_class; - - /* Padding for future expansion */ - void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); - void (*_gtk_reserved3) (void); - void (*_gtk_reserved4) (void); -}; - - -GType foo_tree_store_get_type (void) G_GNUC_CONST; -FooTreeStore *foo_tree_store_new (gint n_columns, - ...); -FooTreeStore *foo_tree_store_newv (gint n_columns, - GType *types); -void foo_tree_store_set_column_types (FooTreeStore *tree_store, - gint n_columns, - GType *types); - -/* NOTE: use gtk_tree_model_get to get values from a FooTreeStore */ - -void foo_tree_store_set_value (FooTreeStore *tree_store, - GtkTreeIter *iter, - gint column, - GValue *value); -void foo_tree_store_set (FooTreeStore *tree_store, - GtkTreeIter *iter, - ...); -void foo_tree_store_set_valuesv (FooTreeStore *tree_store, - GtkTreeIter *iter, - gint *columns, - GValue *values, - gint n_values); -void foo_tree_store_set_valist (FooTreeStore *tree_store, - GtkTreeIter *iter, - va_list var_args); -gboolean foo_tree_store_remove (FooTreeStore *tree_store, - GtkTreeIter *iter); -void foo_tree_store_insert (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint position); -void foo_tree_store_insert_before (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - GtkTreeIter *sibling); -void foo_tree_store_insert_after (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - GtkTreeIter *sibling); -void foo_tree_store_insert_with_values (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint position, - ...); -void foo_tree_store_insert_with_valuesv (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint position, - gint *columns, - GValue *values, - gint n_values); -void foo_tree_store_prepend (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent); -void foo_tree_store_append (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *parent); -gboolean foo_tree_store_is_ancestor (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *descendant); -gint foo_tree_store_iter_depth (FooTreeStore *tree_store, - GtkTreeIter *iter); -void foo_tree_store_clear (FooTreeStore *tree_store); -gboolean foo_tree_store_iter_is_valid (FooTreeStore *tree_store, - GtkTreeIter *iter); -void foo_tree_store_reorder (FooTreeStore *tree_store, - GtkTreeIter *parent, - gint *new_order); -void foo_tree_store_swap (FooTreeStore *tree_store, - GtkTreeIter *a, - GtkTreeIter *b); -void foo_tree_store_move_before (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position); -void foo_tree_store_move_after (FooTreeStore *tree_store, - GtkTreeIter *iter, - GtkTreeIter *position); - - -G_END_DECLS - - -#endif /* __FOO_TREE_STORE_H__ */ diff --git a/lib/profile.c b/lib/profile.c deleted file mode 100644 index 51f328f2..00000000 --- a/lib/profile.c +++ /dev/null @@ -1,605 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, 2006, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "binfile.h" -#include "stackstash.h" -#include "profile.h" -#include "sfile.h" - -struct Profile -{ - StackStash *stash; -}; - -static SFormat * -create_format (void) -{ - SFormat *format; - SForward *object_forward; - SForward *node_forward; - - format = sformat_new(); - - object_forward = sformat_declare_forward (format); - node_forward = sformat_declare_forward (format); - - sformat_set_type ( - format, - sformat_make_record ( - format, "profile", NULL, - sformat_make_integer (format, "size"), - sformat_make_pointer (format, "call_tree", node_forward), - sformat_make_list ( - format, "objects", NULL, - sformat_make_record ( - format, "object", object_forward, - sformat_make_string (format, "name"), - sformat_make_integer (format, "total"), - sformat_make_integer (format, "self"), - NULL)), - sformat_make_list ( - format, "nodes", NULL, - sformat_make_record ( - format, "node", node_forward, - sformat_make_pointer (format, "object", object_forward), - sformat_make_pointer (format, "siblings", node_forward), - sformat_make_pointer (format, "children", node_forward), - sformat_make_pointer (format, "parent", node_forward), - sformat_make_integer (format, "total"), - sformat_make_integer (format, "self"), - sformat_make_integer (format, "toplevel"), - NULL)), - NULL)); - - return format; -} - -static void -serialize_call_tree (StackNode *node, - SFileOutput *output) -{ - if (!node) - return; - - sfile_begin_add_record (output, "node"); - sfile_add_pointer (output, "object", U64_TO_POINTER (node->data)); - sfile_add_pointer (output, "siblings", node->siblings); - sfile_add_pointer (output, "children", node->children); - sfile_add_pointer (output, "parent", node->parent); - sfile_add_integer (output, "total", node->total); - sfile_add_integer (output, "self", node->size); - sfile_add_integer (output, "toplevel", node->toplevel); - sfile_end_add (output, "node", node); - - serialize_call_tree (node->siblings, output); - serialize_call_tree (node->children, output); -} - -gboolean -profile_save (Profile *profile, - const char *file_name, - GError **err) -{ - gboolean result; - - GList *profile_objects; - GList *list; - - SFormat *format = create_format (); - SFileOutput *output = sfile_output_new (format); - - sfile_begin_add_record (output, "profile"); - - sfile_add_integer (output, "size", profile_get_size (profile)); - sfile_add_pointer (output, "call_tree", - stack_stash_get_root (profile->stash)); - - profile_objects = profile_get_objects (profile); - sfile_begin_add_list (output, "objects"); - for (list = profile_objects; list != NULL; list = list->next) - { - ProfileObject *object = list->data; - - sfile_begin_add_record (output, "object"); - - sfile_add_string (output, "name", object->name); - sfile_add_integer (output, "total", object->total); - sfile_add_integer (output, "self", object->self); - - sfile_end_add (output, "object", object->name); - } - g_list_foreach (profile_objects, (GFunc)g_free, NULL); - g_list_free (profile_objects); - - sfile_end_add (output, "objects", NULL); - - sfile_begin_add_list (output, "nodes"); - serialize_call_tree (stack_stash_get_root (profile->stash), output); - sfile_end_add (output, "nodes", NULL); - - sfile_end_add (output, "profile", NULL); - - result = sfile_output_save (output, file_name, err); - - sfile_output_free (output); - sformat_free (format); - - return result; -} - -Profile * -profile_load (const char *filename, GError **err) -{ - SFormat *format; - SFileInput *input; - Profile *profile; - int n, i; - StackNode *root; - - format = create_format (); - input = sfile_load (filename, format, err); - - if (!input) - return NULL; - - profile = g_new (Profile, 1); - - sfile_begin_get_record (input, "profile"); - - sfile_get_integer (input, "size", NULL); - sfile_get_pointer (input, "call_tree", (gpointer *)&root); - - n = sfile_begin_get_list (input, "objects"); - for (i = 0; i < n; ++i) - { - char *string; - - sfile_begin_get_record (input, "object"); - - sfile_get_string (input, "name", &string); - sfile_get_integer (input, "total", NULL); - sfile_get_integer (input, "self", NULL); - - sfile_end_get (input, "object", string); - } - sfile_end_get (input, "objects", NULL); - - profile->stash = stack_stash_new ((GDestroyNotify)g_free); - - n = sfile_begin_get_list (input, "nodes"); - for (i = 0; i < n; ++i) - { - StackNode *node = stack_node_new (profile->stash); - gint32 size; - gint32 total; - - sfile_begin_get_record (input, "node"); - - sfile_get_pointer (input, "object", (gpointer *)&node->data); - sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings); - sfile_get_pointer (input, "children", (gpointer *)&node->children); - sfile_get_pointer (input, "parent", (gpointer *)&node->parent); - sfile_get_integer (input, "total", &total); - sfile_get_integer (input, "self", (gint32 *)&size); - sfile_get_integer (input, "toplevel", NULL); - - node->total = total; - node->size = size; - - sfile_end_get (input, "node", node); - - g_assert (node->siblings != (void *)0x11); - } - sfile_end_get (input, "nodes", NULL); - sfile_end_get (input, "profile", NULL); - - sfile_input_free (input); - sformat_free (format); - - stack_stash_set_root (profile->stash, root); - - return profile; -} - -Profile * -profile_new (StackStash *stash) -{ - Profile *profile = g_new (Profile, 1); - - profile->stash = stack_stash_ref (stash); - - return profile; -} - -static void -add_trace_to_tree (StackLink *trace, gint size, gpointer data) -{ - StackLink *link; - ProfileDescendant *parent = NULL; - ProfileDescendant **tree = data; - - link = trace; - while (link->next) - link = link->next; - - for (; link != NULL; link = link->prev) - { - gpointer address = U64_TO_POINTER (link->data); - ProfileDescendant *prev = NULL; - ProfileDescendant *match = NULL; - - for (match = *tree; match != NULL; prev = match, match = match->siblings) - { - if (match->name == address) - { - if (prev) - { - /* Move to front */ - prev->siblings = match->siblings; - match->siblings = *tree; - *tree = match; - } - break; - } - } - - if (!match) - { - /* Have we seen this object further up the tree? */ - for (match = parent; match != NULL; match = match->parent) - { - if (match->name == address) - break; - } - } - - if (!match) - { - match = g_new (ProfileDescendant, 1); - - match->name = address; - match->cumulative = 0; - match->self = 0; - match->children = NULL; - match->parent = parent; - match->siblings = *tree; - *tree = match; - } - - tree = &(match->children); - parent = match; - } - - parent->self += size; - while (parent) - { - parent->cumulative += size; - parent = parent->parent; - } -} - -ProfileDescendant * -profile_create_descendants (Profile *profile, - char *object_name) -{ - ProfileDescendant *tree = NULL; - - StackNode *node = stack_stash_find_node (profile->stash, object_name); - - while (node) - { - if (node->toplevel) - stack_node_foreach_trace (node, add_trace_to_tree, &tree); - - node = node->next; - } - - return tree; -} - -static ProfileCaller * -profile_caller_new (void) -{ - ProfileCaller *caller = g_new (ProfileCaller, 1); - - caller->next = NULL; - caller->self = 0; - caller->total = 0; - - return caller; -} - -ProfileCaller * -profile_list_callers (Profile *profile, - char *callee_name) -{ - StackNode *node; - StackNode *callees; - GHashTable *callers_by_name; - GHashTable *processed_callers; - ProfileCaller *result = NULL; - - callers_by_name = g_hash_table_new (g_direct_hash, g_direct_equal); - processed_callers = g_hash_table_new (g_direct_hash, g_direct_equal); - - callees = stack_stash_find_node (profile->stash, callee_name); - - for (node = callees; node != NULL; node = node->next) - { - ProfileCaller *caller; - - if (!node->parent) - continue; - - caller = g_hash_table_lookup ( - callers_by_name, U64_TO_POINTER (node->parent->data)); - - if (!caller) - { - caller = profile_caller_new (); - caller->name = U64_TO_POINTER (node->parent->data); - caller->total = 0; - caller->self = 0; - - caller->next = result; - result = caller; - - g_hash_table_insert ( - callers_by_name, U64_TO_POINTER (node->parent->data), caller); - } - } - - for (node = callees; node != NULL; node = node->next) - { - StackNode *top_caller = node->parent; - StackNode *top_callee = node; - StackNode *n; - ProfileCaller *caller; - - if (!node->parent) - continue; - - for (n = node; n && n->parent; n = n->parent) - { - if (n->data == node->data && - n->parent->data == node->parent->data) - { - top_caller = n->parent; - top_callee = n; - } - } - - caller = g_hash_table_lookup ( - callers_by_name, U64_TO_POINTER (node->parent->data)); - - if (!g_hash_table_lookup (processed_callers, top_caller)) - { - caller->total += top_callee->total; - - g_hash_table_insert (processed_callers, top_caller, top_caller); - } - - caller->self += node->size; - } - - g_hash_table_destroy (processed_callers); - g_hash_table_destroy (callers_by_name); - - return result; -} - -#if 0 -/* This code generates a list of all ancestors, rather than - * all callers. It turned out to not work well in practice, - * but on the other hand the single list of callers is not - * all that great either, so we'll keep it around commented - * out for now - */ - -static void -add_to_list (gpointer key, - gpointer value, - gpointer user_data) -{ - GList **list = user_data; - - *list = g_list_prepend (*list, value); -} - -static GList * -listify_hash_table (GHashTable *hash_table) -{ - GList *result = NULL; - - g_hash_table_foreach (hash_table, add_to_list, &result); - - return result; -} - -ProfileCaller * -profile_list_ancestors (Profile *profile, - char *callee_name) -{ - StackNode *callees; - StackNode *node; - GHashTable *callers_by_name; - ProfileCaller *result = NULL; - - callers_by_name = g_hash_table_new (g_direct_hash, g_direct_equal); - - callees = stack_stash_find_node (profile->stash, callee_name); - - for (node = callees; node != NULL; node = node->next) - { - StackNode *n; - gboolean seen_recursive_call; - GHashTable *total_ancestors; - GHashTable *all_ancestors; - GList *all, *list; - - /* Build a list of those ancestors that should get assigned - * totals. If this callee does not have any recursive calls - * higher up, that means all of it's ancestors. If it does - * have a recursive call, only the one between this node - * and the recursive call should get assigned total - */ - seen_recursive_call = FALSE; - all_ancestors = g_hash_table_new (g_direct_hash, g_direct_equal); - total_ancestors = g_hash_table_new (g_direct_hash, g_direct_equal); - for (n = node->parent; n; n = n->parent) - { - if (!seen_recursive_call) - { - g_hash_table_insert (total_ancestors, n->address, n); - } - else - { - g_hash_table_remove (total_ancestors, n->address); - } - - g_hash_table_insert (all_ancestors, n->address, n); - - if (n->address == node->address) - seen_recursive_call = TRUE; - } - - all = listify_hash_table (all_ancestors); - - for (list = all; list; list = list->next) - { - ProfileCaller *caller; - StackNode *ancestor = list->data; - - caller = g_hash_table_lookup (callers_by_name, ancestor->address); - - if (!caller) - { - caller = profile_caller_new (); - g_hash_table_insert ( - callers_by_name, ancestor->address, caller); - caller->name = ancestor->address; - - caller->next = result; - result = caller; - } - - caller->self += node->size; - - if (g_hash_table_lookup (total_ancestors, ancestor->address)) - caller->total += node->total; - } - - g_list_free (all); - g_hash_table_destroy (all_ancestors); - g_hash_table_destroy (total_ancestors); - } - - return result; -} -#endif - -void -profile_free (Profile *profile) -{ - stack_stash_unref (profile->stash); - g_free (profile); -} - -void -profile_descendant_free (ProfileDescendant *descendant) -{ - if (!descendant) - return; - - profile_descendant_free (descendant->siblings); - profile_descendant_free (descendant->children); - - g_free (descendant); -} - -void -profile_caller_free (ProfileCaller *caller) -{ - if (!caller) - return; - - profile_caller_free (caller->next); - g_free (caller); -} - -static int -compute_total (StackNode *node) -{ - StackNode *n; - int total = 0; - - for (n = node; n != NULL; n = n->next) - { - if (n->toplevel) - total += n->total; - } - - return total; -} - -static void -build_object_list (StackNode *node, gpointer data) -{ - GList **objects = data; - ProfileObject *obj; - StackNode *n; - - obj = g_new (ProfileObject, 1); - obj->name = U64_TO_POINTER (node->data); - - obj->total = compute_total (node); - obj->self = 0; - - for (n = node; n != NULL; n = n->next) - obj->self += n->size; - - *objects = g_list_prepend (*objects, obj); -} - -GList * -profile_get_objects (Profile *profile) -{ - GList *objects = NULL; - - stack_stash_foreach_by_address ( - profile->stash, build_object_list, &objects); - - return objects; -} - -gint -profile_get_size (Profile *profile) -{ - StackNode *n; - gint size = 0; - - for (n = stack_stash_get_root (profile->stash); n != NULL; n = n->siblings) - size += n->total; - - return size; -} diff --git a/lib/profile.h b/lib/profile.h deleted file mode 100644 index ed016da2..00000000 --- a/lib/profile.h +++ /dev/null @@ -1,78 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef PROFILE_H -#define PROFILE_H - -#include -#include "stackstash.h" - -typedef struct Profile Profile; -typedef struct ProfileObject ProfileObject; -typedef struct ProfileDescendant ProfileDescendant; -typedef struct ProfileCaller ProfileCaller; - -struct ProfileObject -{ - char * name; /* identifies this object uniquely - * (the pointer itself, not the - * string) - */ - - guint total; /* sum of all toplevel totals */ - guint self; /* sum of all selfs */ -}; - -struct ProfileDescendant -{ - char * name; - guint self; - guint cumulative; - ProfileDescendant * parent; - ProfileDescendant * siblings; - ProfileDescendant * children; -}; - -struct ProfileCaller -{ - char * name; - guint total; - guint self; - - ProfileCaller * next; -}; - -Profile * profile_new (StackStash *stash); -void profile_free (Profile *profile); -gint profile_get_size (Profile *profile); -GList * profile_get_objects (Profile *profile); -ProfileDescendant *profile_create_descendants (Profile *prf, - char *object); -ProfileCaller * profile_list_callers (Profile *profile, - char *object); -void profile_caller_free (ProfileCaller *caller); -void profile_descendant_free (ProfileDescendant *descendant); - -gboolean profile_save (Profile *profile, - const char *file_name, - GError **err); -Profile * profile_load (const char *filename, - GError **err); - -#endif /* PROFILE_H */ diff --git a/lib/resources/libsysprof.gresource.xml b/lib/resources/libsysprof.gresource.xml new file mode 100644 index 00000000..3beeed9f --- /dev/null +++ b/lib/resources/libsysprof.gresource.xml @@ -0,0 +1,11 @@ + + + + ui/sp-callgraph-view.ui + ui/sp-empty-state-view.ui + ui/sp-failed-state-view.ui + ui/sp-process-model-row.ui + ui/sp-profiler-menu-button.ui + ui/sp-recording-state-view.ui + + diff --git a/lib/resources/ui/sp-callgraph-view.ui b/lib/resources/ui/sp-callgraph-view.ui new file mode 100644 index 00000000..2bce8d37 --- /dev/null +++ b/lib/resources/ui/sp-callgraph-view.ui @@ -0,0 +1,198 @@ + + + diff --git a/lib/resources/ui/sp-empty-state-view.ui b/lib/resources/ui/sp-empty-state-view.ui new file mode 100644 index 00000000..fd61eb4e --- /dev/null +++ b/lib/resources/ui/sp-empty-state-view.ui @@ -0,0 +1,64 @@ + + + + diff --git a/lib/resources/ui/sp-failed-state-view.ui b/lib/resources/ui/sp-failed-state-view.ui new file mode 100644 index 00000000..ba2f17ed --- /dev/null +++ b/lib/resources/ui/sp-failed-state-view.ui @@ -0,0 +1,64 @@ + + + + diff --git a/lib/resources/ui/sp-process-model-row.ui b/lib/resources/ui/sp-process-model-row.ui new file mode 100644 index 00000000..8f65dd8a --- /dev/null +++ b/lib/resources/ui/sp-process-model-row.ui @@ -0,0 +1,41 @@ + + + + diff --git a/lib/resources/ui/sp-profiler-menu-button.ui b/lib/resources/ui/sp-profiler-menu-button.ui new file mode 100644 index 00000000..bbb8d367 --- /dev/null +++ b/lib/resources/ui/sp-profiler-menu-button.ui @@ -0,0 +1,221 @@ + + + + + + + + + vertical + true + + + 6 + center + horizontal + stack + true + + + + + true + true + 10 + + + true + Profile my _entire system + true + true + 1 + whole_system_switch + + + + + true + + + end + + + + + + + true + false + true + true + 10 + + + vertical + true + + + + Search + true + + + + + 100 + 400 + 450 + in + true + + + true + + + + + + + existing + Existing Process + + + + + vertical + 12 + true + + + Command Line + true + 0.0 + + + + + + + + + true + + + + + Environment + true + 0.0 + + + + + + + + + true + Inherit current environment + true + + + + + in + 100 + 400 + true + + + environment_model + true + + + true + true + Key + + + true + + + 0 + + + + + + + true + true + Value + + + true + + + 1 + + + + + + + + + + + spawn + New Process + + + + + + + + + + + + + + + + + + + + diff --git a/lib/resources/ui/sp-recording-state-view.ui b/lib/resources/ui/sp-recording-state-view.ui new file mode 100644 index 00000000..0e15477d --- /dev/null +++ b/lib/resources/ui/sp-recording-state-view.ui @@ -0,0 +1,64 @@ + + + + diff --git a/lib/sfile.c b/lib/sfile.c deleted file mode 100644 index a106595a..00000000 --- a/lib/sfile.c +++ /dev/null @@ -1,1180 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#if 0 -#include -#endif -#include "sfile.h" -#include "sformat.h" -#include - -typedef struct BuildContext BuildContext; -typedef struct Instruction Instruction; -typedef enum -{ - BEGIN, END, VALUE -} InstructionKind; - -struct Instruction -{ - InstructionKind kind; - SType *type; - - union - { - struct - { - int n_elements; - int id; - Instruction *end_instruction; - } begin; - - struct - { - Instruction *begin_instruction; - gpointer object; - } end; - - struct - { - int target_id; - Instruction *target_instruction; - gpointer target_object; - gpointer *location; - } pointer; - - struct - { - int value; - } integer; - - struct - { - char *value; - } string; - } u; -}; - -struct BuildContext -{ - SContext *context; - - GArray *instructions; -}; - -static void -set_error (GError **err, - gint code, - const char *format, - va_list args) -{ - char *msg; - - if (!err) - return; - - msg = g_strdup_vprintf (format, args); - - if (*err == NULL) - { - *err = g_error_new_literal (G_MARKUP_ERROR, code, msg); - } - else - { - /* Warning text from GLib */ - g_warning ("GError set over the top of a previous GError or uninitialized memory.\n" - "This indicates a bug in someone's code. You must ensure an error is NULL before it's set.\n" - "The overwriting error message was: %s", - msg); - } - - g_free (msg); -} - -static void -set_unknown_element_error (GError **err, const char *format, ...) -{ - va_list args; - va_start (args, format); - set_error (err, G_MARKUP_ERROR_UNKNOWN_ELEMENT, format, args); - va_end (args); -} - -static void -set_unknown_attribute_error (GError **err, const char *format, ...) -{ - va_list args; - va_start (args, format); - set_error (err, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, format, args); - va_end (args); -} - -static void -set_invalid_content_error (GError **err, const char *format, ...) -{ - va_list args; - va_start (args, format); - set_error (err, G_MARKUP_ERROR_INVALID_CONTENT, format, args); - va_end (args); -} - -/* reading */ -static gboolean -is_all_blank (const char *text) -{ - while (g_ascii_isspace (*text)) - text++; - - return (*text == '\0'); -} - -static gboolean -get_number (const char *text, int *number) -{ - char *end; - int result; - gboolean retval; - - result = strtol (text, &end, 10); - - retval = is_all_blank (end); - - if (retval && number) - *number = result; - - return retval; -} - -struct SFileInput -{ - int n_instructions; - Instruction *instructions; - Instruction *current_instruction; - GHashTable *instructions_by_location; -}; - -static gboolean -check_name (Instruction *instr, - const char *name) -{ - const char *type_name = stype_get_name (instr->type); - - return strcmp (name, type_name) == 0; -} - -void -sfile_begin_get_record (SFileInput *file, const char *name) -{ - Instruction *instruction = file->current_instruction++; - - g_return_if_fail (instruction->kind == BEGIN); - g_return_if_fail (check_name (instruction, name)); - g_return_if_fail (stype_is_record (instruction->type)); -} - -int -sfile_begin_get_list (SFileInput *file, - const char *name) -{ - Instruction *instruction = file->current_instruction++; - - g_return_val_if_fail (instruction->kind == BEGIN, 0); - g_return_val_if_fail (check_name (instruction, name), 0); - g_return_val_if_fail (stype_is_list (instruction->type), 0); - - return instruction->u.begin.n_elements; -} - -void -sfile_get_pointer (SFileInput *file, - const char *name, - gpointer *location) -{ - Instruction *instruction; - - instruction = file->current_instruction++; - g_return_if_fail (stype_is_pointer (instruction->type)); - - instruction->u.pointer.location = location; - - *location = (gpointer) 0xFedeAbe; - - if (location) - { - if (g_hash_table_lookup (file->instructions_by_location, location)) - g_warning ("Reading into the same location twice\n"); - - g_hash_table_insert (file->instructions_by_location, location, instruction); - } - } - -void -sfile_get_integer (SFileInput *file, - const char *name, - gint32 *integer) -{ - Instruction *instruction; - - instruction = file->current_instruction++; - g_return_if_fail (stype_is_integer (instruction->type)); - - if (integer) - *integer = instruction->u.integer.value; -} - -void -sfile_get_string (SFileInput *file, - const char *name, - char **string) -{ - Instruction *instruction; - - instruction = file->current_instruction++; - g_return_if_fail (stype_is_string (instruction->type)); - - if (string) - *string = g_strdup (instruction->u.string.value); -} - -static void -hook_up_pointers (SFileInput *file) -{ - int i; - - for (i = 0; i < file->n_instructions; ++i) - { - Instruction *instruction = &(file->instructions[i]); - - if (stype_is_pointer (instruction->type)) - { - gpointer target_object; - Instruction *target_instruction; - - target_instruction = instruction->u.pointer.target_instruction; - - if (target_instruction) - target_object = target_instruction->u.begin.end_instruction->u.end.object; - else - target_object = NULL; - - *(instruction->u.pointer.location) = target_object; - } - } -} - -void -sfile_end_get (SFileInput *file, - const char *name, - gpointer object) -{ - Instruction *instruction = file->current_instruction++; - - g_return_if_fail (instruction->kind == END); - g_return_if_fail (check_name (instruction, name)); - - instruction->u.end.object = object; - - if (file->current_instruction == file->instructions + file->n_instructions) - hook_up_pointers (file); -} - -static int -get_id (const char **names, const char **values, GError **err) -{ - const char *id_string = NULL; - int id, i; - - for (i = 0; names[i] != NULL; ++i) - { - if (strcmp (names[i], "id") != 0) - { - set_unknown_attribute_error (err, "Unknown attribute: %s", names[i]); - return -1; - } - - if (id_string) - { - set_invalid_content_error (err, "Attribute 'id' defined twice"); - return -1; - } - - id_string = values[i]; - } - - if (!id_string) - return 0; - - if (!get_number (id_string, &id) || id < 1) - { - set_invalid_content_error (err, "Bad attribute value for attribute 'id' (must be >= 1)\n"); - return -1; - } - - return id; -} - -static void -handle_begin_element (GMarkupParseContext *parse_context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **err) -{ - BuildContext *build = user_data; - Instruction instruction; - - instruction.u.begin.id = get_id (attribute_names, attribute_values, err); - - if (instruction.u.begin.id == -1) - return; - - instruction.type = scontext_begin (build->context, element_name); - if (!instruction.type) - { - set_unknown_element_error (err, "<%s> unexpected here", element_name); - return; - } - - if (stype_is_list (instruction.type) || stype_is_record (instruction.type)) - { - instruction.kind = BEGIN; - g_array_append_val (build->instructions, instruction); - } -} - -static void -handle_end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **err) -{ - BuildContext *build = user_data; - Instruction instruction; - - instruction.type = scontext_end (build->context, element_name); - if (!instruction.type) - { - set_unknown_element_error (err, " unexpected here", element_name); - return; - } - - if (stype_is_list (instruction.type) || stype_is_record (instruction.type)) - { - instruction.kind = END; - - g_array_append_val (build->instructions, instruction); - } -} - -static gboolean -decode_text (const char *text, char **decoded) -{ - int length = strlen (text); - - if (length < 2) - return FALSE; - - if (text[0] != '\"' || text[length - 1] != '\"') - return FALSE; - - if (decoded) - *decoded = g_strndup (text + 1, length - 2); - - return TRUE; -} - -static const char * -skip_whitespace (const char *text) -{ - while (g_ascii_isspace (*text)) - text++; - - return text; -} - -static void -handle_text (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **err) -{ - BuildContext *build = user_data; - Instruction instruction; - - if (*text == '\0') - return; - - text = skip_whitespace (text); - if (*text == '\0') - return; - - instruction.type = scontext_text (build->context); - if (!instruction.type) - { - /* FIXME: what are line and ch used for here? */ - int line, ch; - g_markup_parse_context_get_position (context, &line, &ch); - set_invalid_content_error (err, "Unexpected text data"); - return; - } - - instruction.kind = VALUE; - - if (stype_is_pointer (instruction.type)) - { - if (!get_number (text, &instruction.u.pointer.target_id)) - { - set_invalid_content_error (err, "Content '%s' of pointer element is not a number", text); - return; - } - } - else if (stype_is_integer (instruction.type)) - { - if (!get_number (text, &instruction.u.integer.value)) - { - set_invalid_content_error (err, "Content '%s' of integer element is not a number", text); - return; - } - } - else if (stype_is_string (instruction.type)) - { - if (!decode_text (text, &instruction.u.string.value)) - { - set_invalid_content_error (err, "Content '%s' of text element is ill-formed", text); - return; - } - } - else - { - g_assert_not_reached(); - } - - g_array_append_val (build->instructions, instruction); -} - -static void -free_instructions (Instruction *instructions, int n_instructions) -{ - int i; - - for (i = 0; i < n_instructions; ++i) - { - Instruction *instruction = &(instructions[i]); - - if (stype_is_string (instruction->type)) - g_free (instruction->u.string.value); - } - - g_free (instructions); -} - -/* This functions makes end instructions point to the corresponding - * begin instructions, and counts the number of instructions - * contained in a begin/end pair - */ -static Instruction * -process_instruction_pairs (Instruction *first) -{ - Instruction *instruction; - int n_elements; - - g_assert (first->kind == BEGIN); - - instruction = first + 1; - - n_elements = 0; - while (instruction->kind != END) - { - if (instruction->kind == BEGIN) - { - instruction = process_instruction_pairs (instruction); - if (!instruction) - return NULL; - } - else - { - instruction++; - } - - n_elements++; - } - - first->u.begin.n_elements = n_elements; - first->u.begin.end_instruction = instruction; - - instruction->u.end.begin_instruction = first; - - return instruction + 1; -} - -static gboolean -post_process_read_instructions (Instruction *instructions, - int n_instructions, - GError **err) -{ - gboolean retval = TRUE; - GHashTable *instructions_by_id; - int i; - - /* count list instructions, check pointers */ - process_instruction_pairs (instructions); - - /* Build id->instruction map */ - instructions_by_id = g_hash_table_new (g_direct_hash, g_direct_equal); - for (i = 0; i < n_instructions; ++i) - { - Instruction *instruction = &(instructions[i]); - - if (instruction->kind == BEGIN) - { - int id = instruction->u.begin.id; - - if (id) - g_hash_table_insert (instructions_by_id, GINT_TO_POINTER (id), instruction); - } - } - - /* Make pointer instructions point to the corresponding element */ - for (i = 0; i < n_instructions; ++i) - { - Instruction *instruction = &(instructions[i]); - - if (stype_is_pointer (instruction->type)) - { - int target_id = instruction->u.pointer.target_id; - - if (target_id) - { - Instruction *target = g_hash_table_lookup (instructions_by_id, - GINT_TO_POINTER (target_id)); - - if (target) - { - if (stype_get_target_type (instruction->type) == target->type) - { - instruction->u.pointer.target_instruction = target; - } - else - { - set_invalid_content_error (err, "Id %d references an element of the wrong type", - instruction->u.pointer.target_id); - retval = FALSE; - break; - } - } - else - { - set_invalid_content_error (err, "Id %d doesn't reference any record or list", - instruction->u.pointer.target_id); - retval = FALSE; - break; - } - } - else - { - instruction->u.pointer.target_instruction = NULL; - } - } - } - - g_hash_table_destroy (instructions_by_id); - - return retval; -} - -static Instruction * -build_instructions (const char *contents, - gsize length, - SFormat *format, - int *n_instructions, - GError **err) -{ - BuildContext build; - GMarkupParseContext *parse_context; - - GMarkupParser parser = { - handle_begin_element, - handle_end_element, - handle_text, - NULL, /* passthrough */ - NULL, /* error */ - }; - - build.context = scontext_new (format); - build.instructions = g_array_new (TRUE, TRUE, sizeof (Instruction)); - - parse_context = g_markup_parse_context_new (&parser, 0, &build, NULL); - - while (length) - { - int bytes = MIN (length, 65536); - - if (!g_markup_parse_context_parse (parse_context, contents, bytes, err)) - { - free_instructions ((Instruction *)build.instructions->data, build.instructions->len); - return NULL; - } - - contents += bytes; - length -= bytes; - } - - if (!g_markup_parse_context_end_parse (parse_context, err)) - { - free_instructions ((Instruction *)build.instructions->data, build.instructions->len); - return NULL; - } - - if (!scontext_is_finished (build.context)) - { - set_invalid_content_error (err, "Premature end of file\n"); - - free_instructions ((Instruction *)build.instructions->data, build.instructions->len); - return NULL; - } - - if (!post_process_read_instructions ((Instruction *)build.instructions->data, - build.instructions->len, err)) - { - free_instructions ((Instruction *)build.instructions->data, build.instructions->len); - return NULL; - } - - *n_instructions = build.instructions->len; - - return (Instruction *)g_array_free (build.instructions, FALSE); -} - -SFileInput * -sfile_load (const char *filename, - SFormat *format, - GError **err) -{ - gchar *contents; - gsize length; - SFileInput *input; - GMappedFile *file; - - file = g_mapped_file_new (filename, FALSE, err); - if (!file) - return NULL; - - contents = g_mapped_file_get_contents (file); - length = g_mapped_file_get_length (file); - - madvise (contents, length, MADV_SEQUENTIAL); - - input = g_new (SFileInput, 1); - - input->instructions = build_instructions (contents, length, format, &input->n_instructions, err); - - if (!input->instructions) - { - g_free (input); - g_mapped_file_free (file); - return NULL; - } - - g_mapped_file_free (file); - - input->current_instruction = input->instructions; - input->instructions_by_location = g_hash_table_new (g_direct_hash, g_direct_equal); - - return input; -} - -/* Writing */ -struct SFileOutput -{ - SFormat *format; - GArray *instructions; - GHashTable *objects; - SContext *context; -}; - -SFileOutput * -sfile_output_new (SFormat *format) -{ - SFileOutput *output = g_new (SFileOutput, 1); - - output->format = format; - output->instructions = g_array_new (TRUE, TRUE, sizeof (Instruction)); - output->context = scontext_new (format); - output->objects = g_hash_table_new (g_direct_hash, g_direct_equal); - - return output; -} - -void -sfile_begin_add_record (SFileOutput *file, - const char *name) -{ - Instruction instruction; - - instruction.type = scontext_begin (file->context, name); - - g_return_if_fail (instruction.type); - g_return_if_fail (stype_is_record (instruction.type)); - - instruction.kind = BEGIN; - - g_array_append_val (file->instructions, instruction); -} - -void -sfile_begin_add_list (SFileOutput *file, - const char *name) -{ - Instruction instruction; - - instruction.type = scontext_begin (file->context, name); - - g_return_if_fail (instruction.type); - g_return_if_fail (stype_is_list (instruction.type)); - - instruction.kind = BEGIN; - - g_array_append_val (file->instructions, instruction); -} - -void -sfile_end_add (SFileOutput *file, - const char *name, - gpointer object) -{ - Instruction instruction; - - if (object && g_hash_table_lookup (file->objects, object)) - { - g_warning ("Adding the same object (%p) twice", object); - return; - } - - instruction.type = scontext_end (file->context, name); - - if (!instruction.type) - { - g_warning ("invalid call of sfile_end_add()"); - return; - } - - instruction.kind = END; - instruction.u.end.object = object; - - g_array_append_val (file->instructions, instruction); - - if (object) - g_hash_table_insert (file->objects, object, object); -} - -static SType * -sfile_check_value (SFileOutput *file, - const char *name) -{ - SType *tmp_type; - - tmp_type = scontext_begin (file->context, name); - if (!tmp_type) - return NULL; - - tmp_type = scontext_text (file->context); - if (!tmp_type) - return NULL; - - tmp_type = scontext_end (file->context, name); - if (!tmp_type) - return NULL; - - return tmp_type; -} - -void -sfile_add_string (SFileOutput *file, - const char *name, - const char *string) -{ - Instruction instruction; - - g_return_if_fail (g_utf8_validate (string, -1, NULL)); - - instruction.type = sfile_check_value (file, name); - if (!instruction.type || !stype_is_string (instruction.type)) - { - g_warning ("Invalid call to sfile_add_string()"); - return; - } - instruction.kind = VALUE; - instruction.u.string.value = g_strdup (string); - - g_array_append_val (file->instructions, instruction); -} - -void -sfile_add_integer (SFileOutput *file, - const char *name, - int integer) -{ - Instruction instruction; - - instruction.type = sfile_check_value (file, name); - if (!instruction.type || !stype_is_integer (instruction.type)) - { - g_warning ("Invalid call to sfile_add_integer()"); - return; - } - instruction.kind = VALUE; - instruction.u.integer.value = integer; - - g_array_append_val (file->instructions, instruction); -} - -void -sfile_add_pointer (SFileOutput *file, - const char *name, - gpointer pointer) -{ - Instruction instruction; - - instruction.type = sfile_check_value (file, name); - if (!instruction.type || !stype_is_pointer (instruction.type)) - { - g_warning ("Invalid call to sfile_add_pointer()"); - return; - } - instruction.kind = VALUE; - instruction.u.pointer.target_object = pointer; - - g_array_append_val (file->instructions, instruction); -} - -static void -post_process_write_instructions (SFileOutput *sfile) -{ - int i; - Instruction *instructions = (Instruction *)sfile->instructions->data; - int n_instructions = sfile->instructions->len; - int id; - GHashTable *instructions_by_object; - - process_instruction_pairs (instructions); - - /* Set all id's to -1 and create map from objects to instructions */ - - instructions_by_object = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (i = 0; i < n_instructions; ++i) - { - Instruction *instruction = &(instructions[i]); - - if (instruction->kind == BEGIN) - { - instruction->u.begin.id = -1; - } - else if (instruction->kind == END && instruction->u.end.object) - { - g_hash_table_insert (instructions_by_object, - instruction->u.end.object, instruction); - } - } - - /* Assign an id to all pointed-to instructions */ - id = 1; - for (i = 0; i < n_instructions; ++i) - { - Instruction *instruction = &(instructions[i]); - - if (stype_is_pointer (instruction->type)) - { - if (instruction->u.pointer.target_object) - { - Instruction *target; - - target = - g_hash_table_lookup (instructions_by_object, - instruction->u.pointer.target_object); - - if (!target) - { - g_warning ("pointer has unknown target\n"); - goto out; - } - - g_assert (target->kind == END); - - if (target->u.end.begin_instruction->u.begin.id == -1) - target->u.end.begin_instruction->u.begin.id = id++; - - instruction->u.pointer.target_id = - target->u.end.begin_instruction->u.begin.id; - } - else - { - instruction->u.pointer.target_id = 0; - } - } - } - -out: - g_hash_table_destroy (instructions_by_object); -} - -static void -add_indent (GString *output, - int indent) -{ - int i; - - for (i = 0; i < indent; ++i) - g_string_append_c (output, ' '); -} - -static void -add_integer (GString *output, - int value) -{ - g_string_append_printf (output, "%d", value); -} - -static void -add_string (GString *output, - const char *str) -{ - char *escaped = g_markup_escape_text (str, -1); - g_string_append_c (output, '\"'); - g_string_append (output, escaped); - g_string_append_c (output, '\"'); - g_free (escaped); -} - -static void -add_begin_tag (GString *output, - int indent, - const char *name, - int id) -{ - add_indent (output, indent); - - if (id != -1) - g_string_append_printf (output, "<%s id=\"%d\">", name, id); - else - g_string_append_printf (output, "<%s>", name); -} - -static void -add_end_tag (GString *output, - int indent, - const char *name) -{ - add_indent (output, indent); - g_string_append_printf (output, "", name); -} - -static void -add_nl (GString *output) -{ - g_string_append_c (output, '\n'); -} - -#if 0 -static void -disaster (int status) -{ - const char *error; - switch (status) - { - case BZ_PARAM_ERROR: - error = "BZ_PARAM_ERROR"; - break; - - case BZ_MEM_ERROR: - error = "BZ_MEM_ERROR"; - break; - - case BZ_OUTBUFF_FULL: - error = "BZ_OUTBUFF_FULL"; - break; - - default: - error = "Unknown error"; - break; - } - - g_error ("Failed to compress file: %s\n", error); -} - -static void -bz2_compress (const guchar *input, int input_length, - guchar **output, int *output_length) -{ - size_t compressed_size; - guchar *compressed_data; - int status; - - g_return_if_fail (input != NULL); - - /* The bzip2 manual says: - * - * To guarantee that the compressed data will fit in its buffer, - * allocate an output buffer of size 1% larger than the uncompressed - * data, plus six hundred extra bytes. - */ - compressed_size = (size_t)(1.02 * input_length + 600); - compressed_data = g_malloc (compressed_size); - - status = BZ2_bzBuffToBuffCompress (compressed_data, &compressed_size, - (guchar *)input, input_length, - 9 /* block size */, - 0 /* verbosity */, - 0 /* workfactor */); - - if (status != BZ_OK) - disaster (status); - - if (output) - *output = compressed_data; - else - g_free (compressed_data); - - if (output_length) - *output_length = compressed_size; -} -#endif - -static const char * -get_name (Instruction *inst) -{ - return stype_get_name (inst->type); -} - -gboolean -sfile_output_save (SFileOutput *sfile, - const char *filename, - GError **err) -{ - int i; - Instruction *instructions; - GString *output; - int indent; - gboolean retval; -#if 0 - guchar *compressed; - size_t compressed_size; -#endif - - g_return_val_if_fail (sfile != NULL, FALSE); - - instructions = (Instruction *)sfile->instructions->data; - - post_process_write_instructions (sfile); - - indent = 0; - output = g_string_new (""); - for (i = 0; i < sfile->instructions->len; ++i) - { - Instruction *instruction = &(instructions[i]); - - switch (instruction->kind) - { - case BEGIN: - add_begin_tag (output, indent, get_name (instruction), - instruction->u.begin.id); - add_nl (output); - indent += 4; - break; - - case END: - indent -= 4; - add_end_tag (output, indent, get_name (instruction)); - add_nl (output); - break; - - case VALUE: - add_begin_tag (output, indent, get_name (instruction), -1); - - if (stype_is_integer (instruction->type)) - { - add_integer (output, instruction->u.integer.value); - } - else if (stype_is_pointer (instruction->type)) - { - add_integer (output, instruction->u.pointer.target_id); - } - else if (stype_is_string (instruction->type)) - { - add_string (output, instruction->u.string.value); - } - - add_end_tag (output, 0, get_name (instruction)); - - add_nl (output); - break; - } - } - -#if 0 - /* FIXME - not10: bz2 compressing the output is probably - * interesting at some point. For now just make sure - * it works without actually using it. - */ - bz2_compress (output->str, output->len, - &compressed, &compressed_size); - - g_free (compressed); -#endif - - retval = g_file_set_contents (filename, output->str, output->len, err); - - g_string_free (output, TRUE); - - return retval; -} - - -void -sfile_input_free (SFileInput *file) -{ - free_instructions (file->instructions, file->n_instructions); - - g_hash_table_destroy (file->instructions_by_location); - - g_free (file); -} - -void -sfile_output_free (SFileOutput *sfile) -{ - Instruction *instructions; - int n_instructions; - - n_instructions = sfile->instructions->len; - instructions = (Instruction *)g_array_free (sfile->instructions, FALSE); - - free_instructions (instructions, n_instructions); - - g_hash_table_destroy (sfile->objects); - g_free (sfile); -} diff --git a/lib/sfile.h b/lib/sfile.h deleted file mode 100644 index 20f8aa55..00000000 --- a/lib/sfile.h +++ /dev/null @@ -1,169 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "sformat.h" - -typedef struct SFileInput SFileInput; -typedef struct SFileOutput SFileOutput; - - -#if 0 -Serializer *serializer_new (const char *version); -void serializer_set_format (Serializer *serializer, - SerializerFormat *format); -SerializerFormat *serializer_make_list (Serializer *serializer, - const char *name, - SerializerFormat *contents); -SerializerFormat *serializer_make_record (Serializer *serializer, - const char *name, - SerializerFormat *contents1, - ...); -SerializerFormat *serializer_make_integer (Serializer *serialiser, - const char *name); -SerializerFormat *serializer_make_pointer (Serializer *serialiser, - const char *name, - SerializerFormat *target_type); -#endif - -/* A possibly better API/naming scheme - * - * Serializer *serializer_new (SerializerFormat *format); - * - * SerializerReadContext *serializer_begin_read_filename (serializer *serialize, - * const char *filename, - * GError *err); - * serializer_get_blah (SerializerReadContext *); - * void serialzier_end_read (...); - * - * SerializerWritecontext *...; - * serializer_begin_write (context); - * serializer_write_int (); - * serializer_end_write (..., GError **err); - * - * - * For formats consider: - * - * Format *format_new (void); - * void format_free (void); - * Content *format_create_record (Format *format, Content *c1, ...); - * Content *format_create_list (Format *format, Content *t); - * Content *format_create_pointer (Format *format, Content *pointer_type); - * - * void format_set_record (Format *f, Content *r); - * Content *new_record (Content *c1, ...); - * - * List *format_new_list (Format *f - * - * - * Consider adding optional elements: - * - * sformat_new_optional (gpointer content) - * - * enums, optionals, selections, empties - * - * - * Other things: - * - * "selections" - when several different types are possible - would need lambda transitions in and out - * - * ability to allow 'ignored' elements that are simply skipped at parse time. This could become important - * for future-proofing files. - * - * unions maybe? - * - * - * - * - *============================================== - * Also think about versioning - apps will want to be able to read and write - * different versions of the format, and they want to be able to sniff the - * format + version - * - * The version should be part of the format. There should be a - * const char *sfile_sniff (const filename); - * that will return NULL (+ error) if the file can't be parsed - * - */ - -/* - Describing Types - */ - - -/* - Reading - */ -SFileInput * sfile_load (const char *filename, - SFormat *format, - GError **err); -void sfile_begin_get_record (SFileInput *file, - const char *name); -int sfile_begin_get_list (SFileInput *file, - const char *name); -void sfile_get_pointer (SFileInput *file, - const char *name, - gpointer *pointer); -void sfile_get_integer (SFileInput *file, - const char *name, - gint32 *integer); -void sfile_get_string (SFileInput *file, - const char *name, - char **string); -void sfile_end_get (SFileInput *file, - const char *name, - gpointer object); -void sfile_input_free (SFileInput *file); - -#if 0 -/* incremental loading (worth considering at least) */ -SFileLoader *sfile_loader_new (SFormat *format); -void sfile_loader_add_text (SFileLoader *loader, - const char *text, - int len); -SFile * sfile_loader_finish (SFileLoader *loader, - GError **err); -void sfile_loader_free (SFileLoader *loader); -#endif - -/* - Writing - */ - -/* FIXME - not10: see if we can't get rid of the names. It - * should be possible to pass NULL to state_transition_check() - * and have it interprete that as "whatever". We would need - * a way to get the name back then, though. - */ - -SFileOutput * sfile_output_new (SFormat *format); -void sfile_begin_add_record (SFileOutput *file, - const char *name); -void sfile_begin_add_list (SFileOutput *file, - const char *name); -void sfile_end_add (SFileOutput *file, - const char *name, - gpointer object); -void sfile_add_string (SFileOutput *file, - const char *name, - const char *string); -void sfile_add_integer (SFileOutput *file, - const char *name, - int integer); -void sfile_add_pointer (SFileOutput *file, - const char *name, - gpointer pointer); -gboolean sfile_output_save (SFileOutput *sfile, - const char *filename, - GError **err); - -void sfile_output_free (SFileOutput *sfile); diff --git a/lib/sformat.c b/lib/sformat.c deleted file mode 100644 index 08a18bd2..00000000 --- a/lib/sformat.c +++ /dev/null @@ -1,646 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, 2006, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include - -#include "sformat.h" - -typedef struct State State; -typedef struct Transition Transition; -typedef struct Fragment Fragment; - -/* - * Format - */ -struct SFormat -{ - State * begin; - State * end; - - GQueue * types; - GQueue * transitions; - GQueue * states; -}; - -/* - * Type - */ -typedef enum -{ - TYPE_POINTER, - TYPE_STRING, - TYPE_INTEGER, - TYPE_RECORD, - TYPE_LIST, - TYPE_FORWARD -} TypeKind; - -struct SType -{ - TypeKind kind; - char *name; - Transition *enter, *exit; - SType *target; /* If kind is TYPE_POINTER */ -}; - -static void type_free (SType *type); - -/* - * Transition - */ -typedef enum -{ - BEGIN, - VALUE, - END -} TransitionKind; - -struct Transition -{ - SType * type; - TransitionKind kind; - State * to; -}; - -static Transition *transition_new (SFormat *format, - TransitionKind kind, - SType *type, - State *from, - State *to); -static void transition_free (Transition *transition); -static void transition_set_to_state (Transition *transition, - State *to_state); - -/* - * State - */ - -struct State -{ - GQueue *transitions; -}; - -static State *state_new (SFormat *format); -static void state_add_transition (State *state, - Transition *transition); -static void state_free (State *state); - -/* - * Format - */ -SFormat * -sformat_new (void) -{ - /* FIXME: should probably be refcounted, and an SContext - * should have a ref on the format - */ - SFormat *format = g_new0 (SFormat, 1); - - format->begin = NULL; - format->end = NULL; - - format->types = g_queue_new (); - format->states = g_queue_new (); - format->transitions = g_queue_new (); - - return format; -} - -void -sformat_free (SFormat *format) -{ - GList *list; - - for (list = format->types->head; list; list = list->next) - { - SType *type = list->data; - - type_free (type); - } - g_queue_free (format->types); - - for (list = format->states->head; list; list = list->next) - { - State *state = list->data; - - state_free (state); - } - g_queue_free (format->states); - - for (list = format->transitions->head; list; list = list->next) - { - Transition *transition = list->data; - - transition_free (transition); - } - g_queue_free (format->transitions); - - g_free (format); -} - -void -sformat_set_type (SFormat *format, - SType *type) -{ - format->begin = state_new (format); - format->end = state_new (format); - - state_add_transition (format->begin, type->enter); - transition_set_to_state (type->exit, format->end); -} - -/* - * Type - */ -static SType * -type_new (SFormat *format, - TypeKind kind, - const char *name) -{ - SType *type = g_new0 (SType, 1); - - type->kind = kind; - type->name = name? g_strdup (name) : NULL; - type->enter = NULL; - type->exit = NULL; - type->target = NULL; - - g_queue_push_tail (format->types, type); - - return type; -} - -static SType * -type_new_from_forward (SFormat *format, - TypeKind kind, - const char *name, - SForward *forward) -{ - SType *type; - - if (forward) - { - type = (SType *)forward; - type->kind = kind; - type->name = g_strdup (name); - } - else - { - type = type_new (format, kind, name); - } - - return type; -} - - -static SType * -type_new_value (SFormat *format, TypeKind kind, const char *name) -{ - SType *type = type_new (format, kind, name); - State *before, *after; - Transition *value; - - before = state_new (format); - after = state_new (format); - - type->enter = transition_new (format, BEGIN, type, NULL, before); - type->exit = transition_new (format, END, type, after, NULL); - value = transition_new (format, VALUE, type, before, after); - - return type; -} - -static void -type_free (SType *type) -{ - g_free (type->name); - g_free (type); -} - -SForward * -sformat_declare_forward (SFormat *format) -{ - SType *type = type_new (format, TYPE_FORWARD, NULL); - - return (SForward *)type; -} - - -static GQueue * -expand_varargs (SType *content1, - va_list args) -{ - GQueue *types = g_queue_new (); - SType *type; - - g_queue_push_tail (types, content1); - - type = va_arg (args, SType *); - while (type) - { - g_queue_push_tail (types, type); - type = va_arg (args, SType *); - } - - return types; -} - -SType * -sformat_make_record (SFormat *format, - const char *name, - SForward *forward, - SType *content, - ...) -{ - SType *type; - va_list args; - GQueue *types; - GList *list; - State *begin, *state; - - /* Build queue of child types */ - va_start (args, content); - types = expand_varargs (content, args); - va_end (args); - - /* chain types together */ - state = begin = state_new (format); - - for (list = types->head; list != NULL; list = list->next) - { - SType *child_type = list->data; - - state_add_transition (state, child_type->enter); - - state = state_new (format); - - transition_set_to_state (child_type->exit, state); - } - - g_queue_free (types); - - /* create and return the new type */ - type = type_new_from_forward (format, TYPE_RECORD, name, forward); - type->enter = transition_new (format, BEGIN, type, NULL, begin); - type->exit = transition_new (format, END, type, state, NULL); - - return type; -} - -SType * -sformat_make_list (SFormat *format, - const char *name, - SForward *forward, - SType *child_type) -{ - SType *type; - State *list_state; - - type = type_new_from_forward (format, TYPE_LIST, name, forward); - - list_state = state_new (format); - - type->enter = transition_new (format, BEGIN, type, NULL, list_state); - type->exit = transition_new (format, END, type, list_state, NULL); - - state_add_transition (list_state, child_type->enter); - transition_set_to_state (child_type->exit, list_state); - - return type; -} - -SType * -sformat_make_pointer (SFormat *format, - const char *name, - SForward *forward) -{ - SType *type = type_new_value (format, TYPE_POINTER, name); - type->target = (SType *)forward; - - return type; -} - -SType * -sformat_make_integer (SFormat *format, - const char *name) -{ - return type_new_value (format, TYPE_INTEGER, name); -} - -SType * -sformat_make_string (SFormat *format, - const char *name) -{ - return type_new_value (format, TYPE_STRING, name); -} - - - -gboolean -stype_is_record (SType *type) -{ - return type->kind == TYPE_RECORD; -} - -gboolean -stype_is_list (SType *type) -{ - return type->kind == TYPE_LIST; -} - -gboolean -stype_is_pointer (SType *type) -{ - return type->kind == TYPE_POINTER; -} - -gboolean -stype_is_integer (SType *type) -{ - return type->kind == TYPE_INTEGER; -} - -gboolean -stype_is_string (SType *type) -{ - return type->kind == TYPE_STRING; -} - -SType * -stype_get_target_type (SType *type) -{ - g_return_val_if_fail (stype_is_pointer (type), NULL); - - return type->target; -} - -const char * -stype_get_name (SType *type) -{ - return type->name; -} - -/* Consider adding unions at some point - * - * To be useful they should probably be anonymous, so that - * the union itself doesn't have a representation in the - * xml file. - * - * API: - * sformat_new_union (gpointer content1, ...); - * - * char *content = begin_get_union (); - * if (strcmp (content, ...) == 0) - * get_pointer (); - * else if (strcmp (content, ...) == 0) - * - * ; - * - * Annoying though, that we then won't have the nice one-to-one - * correspondence between begin()/end() calls and s - * Actually, we will probably have to have asdlfkj - * elements. That will make things a lot easier, and unions are - * still pretty useful if you put big things like lists in them. - * - * Or maybe just give them a name ... - * - * We may also consider adding anonymous records. These will - * not be able to have pointers associated with them though - * (because there wouldn't be a natural place - * - * - * Also consider adding the following data types: - * - * - Binary blobs of data, stored as base64 perhaps - * - * - floating point values. How do we store those portably - * without losing precision? Gnumeric may know. - * - * - enums, stored as strings - * - * - booleans. - */ - - -/* - * State - */ -static State * -state_new (SFormat *format) -{ - State *state = g_new0 (State, 1); - state->transitions = g_queue_new (); - - g_queue_push_tail (format->states, state); - - return state; -} - -static void -state_add_transition (State *state, - Transition *transition) -{ - g_queue_push_tail (state->transitions, transition); -} - -static void -state_free (State *state) -{ - g_queue_free (state->transitions); - g_free (state); -} - -/* - * Transition - */ -static Transition * -transition_new (SFormat *format, - TransitionKind kind, - SType *type, - State *from, - State *to) -{ - Transition *transition = g_new0 (Transition, 1); - - transition->type = type; - transition->kind = kind; - transition->to = to; - - if (from) - state_add_transition (from, transition); - - g_queue_push_tail (format->transitions, transition); - - return transition; -} - -static void -transition_free (Transition *transition) -{ - g_free (transition); -} - -static void -transition_set_to_state (Transition *transition, - State *to_state) -{ - transition->to = to_state; -} - -/* Context */ -struct SContext -{ - SFormat *format; - State *state; -}; - -SContext * -scontext_new (SFormat *format) -{ - SContext *context = g_new0 (SContext, 1); - - context->format = format; - context->state = format->begin; - - return context; -} - -static SType * -do_transition (SContext *context, - TransitionKind kind, - const char *element) -{ - GList *list; - - for (list = context->state->transitions->head; list; list = list->next) - { - Transition *transition = list->data; - - if (transition->kind == kind) - { - if (kind == VALUE || strcmp (transition->type->name, element) == 0) - { - context->state = transition->to; - return transition->type; - } - } - } - - return NULL; -} - -SType * -scontext_begin (SContext *context, - const char *element) -{ - return do_transition (context, BEGIN, element); -} - -SType * -scontext_text (SContext *context) -{ - return do_transition (context, VALUE, NULL); -} - -SType * -scontext_end (SContext *context, - const char *element) -{ - return do_transition (context, END, element); -} - -gboolean -scontext_is_finished (SContext *context) -{ - return context->state == context->format->end; -} - -void -scontext_free (SContext *context) -{ - g_free (context); -} - - - -/* assorted stuff */ -#if 0 -static const State * -state_transition_check (const State *state, - const char *element, - TransitionKind kind, - SType *type) -{ - GList *list; - - for (list = state->transitions->head; list; list = list->next) - { - Transition *transition = list->data; - - if (transition->kind == kind && - strcmp (element, transition->element) == 0) - { - *type = transition->type; - return transition->to; - } - } - - return NULL; -} - -static const State * -state_transition_begin (const State *state, const char *element, SType *type) -{ - return state_transition_check (state, element, BEGIN, type); -} - -static const State * -state_transition_end (const State *state, const char *element, SType *type) -{ - return state_transition_check (state, element, END, type); -} - -static const State * -state_transition_text (const State *state, SType *type, SType *target_type) -{ - GList *list; - - for (list = state->transitions->head; list; list = list->next) - { - Transition *transition = list->data; - - if (transition->kind == VALUE) - { - *type = transition->type; - - if (*type == TYPE_POINTER && target_type) - *target_type = transition->target_type; - - /* There will never be more than one allowed value transition for - * a given state - */ - return transition->to; - } - } - - return NULL; -} - -#endif diff --git a/lib/sformat.h b/lib/sformat.h deleted file mode 100644 index 484f7a61..00000000 --- a/lib/sformat.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, 2006, Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef SFORMAT_H -#define SFORMAT_H - -typedef struct SFormat SFormat; -typedef struct SType SType; -typedef struct SForward SForward; -typedef struct SContext SContext; - -SFormat *sformat_new (void); -void sformat_free (SFormat *format); -void sformat_set_type (SFormat *format, - SType *type); -SForward *sformat_declare_forward (SFormat *format); -SType *sformat_make_record (SFormat *format, - const char *name, - SForward *forward, - SType *content, - ...); -SType *sformat_make_list (SFormat *format, - const char *name, - SForward *forward, - SType *type); -SType *sformat_make_pointer (SFormat *format, - const char *name, - SForward *type); -SType *sformat_make_integer (SFormat *format, - const char *name); -SType *sformat_make_string (SFormat *format, - const char *name); - -gboolean stype_is_record (SType *type); -gboolean stype_is_list (SType *type); -gboolean stype_is_pointer (SType *type); -gboolean stype_is_integer (SType *type); -gboolean stype_is_string (SType *type); -SType *stype_get_target_type (SType *type); -const char *stype_get_name (SType *type); - -SContext *scontext_new (SFormat *format); -SType *scontext_begin (SContext *context, - const char *element); -SType *scontext_text (SContext *context); -SType *scontext_end (SContext *context, - const char *element); -gboolean scontext_is_finished (SContext *context); -void scontext_free (SContext *context); - -#endif diff --git a/lib/signal-handler.c b/lib/signal-handler.c deleted file mode 100644 index a241d253..00000000 --- a/lib/signal-handler.c +++ /dev/null @@ -1,215 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk) - * - * 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 2 of the License, 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; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include "watch.h" -#include "signal-handler.h" - -typedef struct SignalWatch SignalWatch; -struct SignalWatch -{ - int signo; - SignalFunc handler; - gpointer user_data; - - struct sigaction old_action; - - SignalWatch * next; -}; - -static int read_end = -1; -static int write_end = -1; -static SignalWatch *signal_watches = NULL; - -static SignalWatch * -lookup_signal_watch (int signo) -{ - SignalWatch *w; - - for (w = signal_watches; w != NULL; w = w->next) - { - if (w->signo == signo) - return w; - } - - return NULL; -} - -static void -remove_signal_watch (SignalWatch *watch) -{ - SignalWatch *prev, *w; - - g_return_if_fail (watch != NULL); - - prev = NULL; - for (w = signal_watches; w != NULL; w = w->next) - { - if (w == watch) - { - if (prev) - prev->next = w->next; - else - signal_watches = w->next; - - break; - } - - prev = w; - } -} - -static void -signal_handler (int signo, - siginfo_t *info, - void *data) -{ - /* FIXME: I suppose we should handle short - * and non-successful writes ... - * - * And also, there is a deadlock if so many signals arrive that - * write() blocks. Then we will be stuck right here, and the - * main loop will never run. Kinda hard to fix without dropping - * signals ... - * - */ - write (write_end, &signo, sizeof (int)); -} - -static void -on_read (gpointer data) -{ - SignalWatch *watch; - int signo; - - /* FIXME: handle short read I suppose */ - read (read_end, &signo, sizeof (int)); - - watch = lookup_signal_watch (signo); - - if (watch) - watch->handler (signo, watch->user_data); -} - -static gboolean -create_pipe (int *read_end, - int *write_end, - GError **err) -{ - int p[2]; - - if (pipe (p) < 0) - { - /* FIXME - create an error */ - return FALSE; - } - - /* FIXME: We should probably make the fd's non-blocking */ - if (read_end) - *read_end = p[0]; - - if (write_end) - *write_end = p[1]; - - return TRUE; -} - -static gboolean -install_signal_handler (int signo, - struct sigaction *old_action, - GError **err) -{ - struct sigaction action; - - memset (&action, 0, sizeof (action)); - - action.sa_sigaction = signal_handler; - sigemptyset (&action.sa_mask); - action.sa_flags = SA_SIGINFO; - - if (sigaction (signo, &action, old_action) < 0) - { - /* FIXME - create an error */ - return TRUE; - } - - return TRUE; -} - -static void -signal_watch_free (SignalWatch *watch) -{ - remove_signal_watch (watch); - - g_free (watch); -} - -gboolean -signal_set_handler (int signo, - SignalFunc handler, - gpointer data, - GError **err) -{ - SignalWatch *watch; - - g_return_val_if_fail (lookup_signal_watch (signo) == NULL, FALSE); - - if (read_end == -1) - { - if (!create_pipe (&read_end, &write_end, err)) - return FALSE; - - fd_add_watch (read_end, NULL); - fd_set_read_callback (read_end, on_read); - } - - watch = g_new0 (SignalWatch, 1); - - watch->signo = signo; - watch->handler = handler; - watch->user_data = data; - watch->next = signal_watches; - signal_watches = watch; - - if (!install_signal_handler (signo, &watch->old_action, err)) - { - signal_watch_free (watch); - - return FALSE; - } - - return TRUE; -} - -void -signal_unset_handler (int signo) -{ - SignalWatch *watch; - - watch = lookup_signal_watch (signo); - - g_return_if_fail (watch != NULL); - - sigaction (signo, &watch->old_action, NULL); - - signal_watch_free (watch); -} diff --git a/lib/signal-handler.h b/lib/signal-handler.h deleted file mode 100644 index 44f3dde1..00000000 --- a/lib/signal-handler.h +++ /dev/null @@ -1,27 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk) - * - * 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 2 of the License, 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; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -typedef void (* SignalFunc) (int signo, gpointer data); - -gboolean signal_set_handler (int signo, - SignalFunc handler, - gpointer data, - GError **err); -void signal_unset_handler (int signo); diff --git a/lib/sp-address.c b/lib/sp-address.c new file mode 100644 index 00000000..b4312354 --- /dev/null +++ b/lib/sp-address.c @@ -0,0 +1,91 @@ +/* sp-address.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-address.h" + +gboolean +sp_address_is_context_switch (SpAddress address, + SpAddressContext *context) +{ + SpAddressContext dummy; + + if (context == NULL) + context = &dummy; + + switch (address) + { + case PERF_CONTEXT_HV: + *context = SP_ADDRESS_CONTEXT_HYPERVISOR; + return TRUE; + + case PERF_CONTEXT_KERNEL: + *context = SP_ADDRESS_CONTEXT_KERNEL; + return TRUE; + + case PERF_CONTEXT_USER: + *context = SP_ADDRESS_CONTEXT_USER; + return TRUE; + + case PERF_CONTEXT_GUEST: + *context = SP_ADDRESS_CONTEXT_GUEST; + return TRUE; + + case PERF_CONTEXT_GUEST_KERNEL: + *context = SP_ADDRESS_CONTEXT_GUEST_KERNEL; + return TRUE; + + case PERF_CONTEXT_GUEST_USER: + *context = SP_ADDRESS_CONTEXT_GUEST_USER; + return TRUE; + + default: + *context = SP_ADDRESS_CONTEXT_NONE; + return FALSE; + } +} + +const gchar * +sp_address_context_to_string (SpAddressContext context) +{ + switch (context) + { + case SP_ADDRESS_CONTEXT_HYPERVISOR: + return "- - hypervisor - -"; + + case SP_ADDRESS_CONTEXT_KERNEL: + return "- - kernel - -"; + + case SP_ADDRESS_CONTEXT_USER: + return "- - user - -"; + + case SP_ADDRESS_CONTEXT_GUEST: + return "- - guest - -"; + + case SP_ADDRESS_CONTEXT_GUEST_KERNEL: + return "- - guest kernel - -"; + + case SP_ADDRESS_CONTEXT_GUEST_USER: + return "- - guest user - -"; + + case SP_ADDRESS_CONTEXT_NONE: + default: + return "- - unknown - -"; + } +} diff --git a/lib/sp-address.h b/lib/sp-address.h new file mode 100644 index 00000000..82348427 --- /dev/null +++ b/lib/sp-address.h @@ -0,0 +1,59 @@ +/* sp-address.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_ADDRESS_H +#define SP_ADDRESS_H + +#include + +G_BEGIN_DECLS + +typedef guint64 SpAddress; + +G_STATIC_ASSERT (sizeof (SpAddress) >= sizeof (gpointer)); + +typedef enum +{ + SP_ADDRESS_CONTEXT_NONE = 0, + SP_ADDRESS_CONTEXT_HYPERVISOR, + SP_ADDRESS_CONTEXT_KERNEL, + SP_ADDRESS_CONTEXT_USER, + SP_ADDRESS_CONTEXT_GUEST, + SP_ADDRESS_CONTEXT_GUEST_KERNEL, + SP_ADDRESS_CONTEXT_GUEST_USER, +} SpAddressContext; + +gboolean sp_address_is_context_switch (SpAddress address, + SpAddressContext *context); +const gchar *sp_address_context_to_string (SpAddressContext context); + +static inline gint +sp_address_compare (SpAddress a, + SpAddress b) +{ + if (a < b) + return -1; + else if (a == b) + return 0; + else + return 1; +} + +G_END_DECLS + +#endif /* SP_ADDRESS_H */ diff --git a/lib/sp-callgraph-profile-private.h b/lib/sp-callgraph-profile-private.h new file mode 100644 index 00000000..f7955c18 --- /dev/null +++ b/lib/sp-callgraph-profile-private.h @@ -0,0 +1,31 @@ +/* sp-callgraph-profile-private.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_CALLGRAPH_PROFILE_PRIVATE_H +#define SP_CALLGRAPH_PROFILE_PRIVATE_H + +#include "sp-callgraph-profile.h" +#include "stackstash.h" + +G_BEGIN_DECLS + +StackStash *sp_callgraph_profile_get_stash (SpCallgraphProfile *self); + +G_END_DECLS + +#endif /* SP_CALLGRAPH_PROFILE_PRIVATE_H */ diff --git a/lib/sp-callgraph-profile.c b/lib/sp-callgraph-profile.c new file mode 100644 index 00000000..996c53e3 --- /dev/null +++ b/lib/sp-callgraph-profile.c @@ -0,0 +1,376 @@ +/* sp-callgraph-profile.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 . + */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2009-2012 Soeren Sandmann and others + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "sp-address.h" +#include "sp-callgraph-profile.h" +#include "sp-callgraph-profile-private.h" +#include "sp-capture-reader.h" +#include "sp-elf-symbol-resolver.h" +#include "sp-jitmap-symbol-resolver.h" +#include "sp-map-lookaside.h" +#include "sp-kernel-symbol-resolver.h" + +#include "stackstash.h" + +struct _SpCallgraphProfile +{ + GObject parent_instance; + + SpCaptureReader *reader; + GStringChunk *symbols; + StackStash *stash; + GHashTable *tags; +}; + +static void profile_iface_init (SpProfileInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpCallgraphProfile, sp_callgraph_profile, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (SP_TYPE_PROFILE, profile_iface_init)) + +SpProfile * +sp_callgraph_profile_new (void) +{ + return g_object_new (SP_TYPE_CALLGRAPH_PROFILE, NULL); +} + +static void +sp_callgraph_profile_finalize (GObject *object) +{ + SpCallgraphProfile *self = (SpCallgraphProfile *)object; + + g_clear_pointer (&self->symbols, g_string_chunk_free); + g_clear_pointer (&self->stash, stack_stash_unref); + g_clear_pointer (&self->reader, sp_capture_reader_unref); + g_clear_pointer (&self->tags, g_hash_table_unref); + + G_OBJECT_CLASS (sp_callgraph_profile_parent_class)->finalize (object); +} + +static void +sp_callgraph_profile_class_init (SpCallgraphProfileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_callgraph_profile_finalize; +} + +static void +sp_callgraph_profile_init (SpCallgraphProfile *self) +{ + self->symbols = g_string_chunk_new (getpagesize ()); + self->tags = g_hash_table_new (g_str_hash, g_str_equal); +} + +static void +sp_callgraph_profile_set_reader (SpProfile *profile, + SpCaptureReader *reader) +{ + SpCallgraphProfile *self = (SpCallgraphProfile *)profile; + + g_assert (SP_IS_CALLGRAPH_PROFILE (self)); + g_assert (reader != NULL); + + g_clear_pointer (&self->reader, sp_capture_reader_unref); + self->reader = sp_capture_reader_ref (reader); +} + +static const gchar * +sp_callgraph_profile_intern_string_take (SpCallgraphProfile *self, + gchar *str) +{ + const gchar *ret; + + g_assert (SP_IS_CALLGRAPH_PROFILE (self)); + g_assert (str != NULL); + + ret = g_string_chunk_insert_const (self->symbols, str); + g_free (str); + return ret; +} + +static const gchar * +sp_callgraph_profile_intern_string (SpCallgraphProfile *self, + const gchar *str) +{ + g_assert (SP_IS_CALLGRAPH_PROFILE (self)); + g_assert (str != NULL); + + return g_string_chunk_insert_const (self->symbols, str); +} + +static void +sp_callgraph_profile_generate_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SpCallgraphProfile *self = source_object; + SpCaptureReader *reader = task_data; + g_autoptr(GArray) resolved = NULL; + g_autoptr(GHashTable) maps_by_pid = NULL; + g_autoptr(GHashTable) cmdlines = NULL; + g_autoptr(GPtrArray) resolvers = NULL; + SpCaptureFrameType type; + StackStash *stash; + StackStash *resolved_stash; + gboolean ret = FALSE; + guint j; + + g_assert (G_IS_TASK (task)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + maps_by_pid = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sp_map_lookaside_free); + cmdlines = g_hash_table_new (NULL, NULL); + + stash = stack_stash_new (NULL); + resolved_stash = stack_stash_new (NULL); + + resolvers = g_ptr_array_new_with_free_func (g_object_unref); + g_ptr_array_add (resolvers, sp_kernel_symbol_resolver_new ()); + g_ptr_array_add (resolvers, sp_elf_symbol_resolver_new ()); + g_ptr_array_add (resolvers, sp_jitmap_symbol_resolver_new ()); + + for (j = 0; j < resolvers->len; j++) + { + SpSymbolResolver *resolver = g_ptr_array_index (resolvers, j); + + sp_capture_reader_reset (reader); + sp_symbol_resolver_load (resolver, reader); + } + + sp_capture_reader_reset (reader); + + /* + * The resolved pointer array is where we stash the names for the + * instruction pointers to pass to the stash stack. All the strings + * need to be deduplicated so that pointer comparison works as if we + * did instruction-pointer comparison. + */ + resolved = g_array_new (FALSE, TRUE, sizeof (guint64)); + + while (sp_capture_reader_peek_type (reader, &type)) + { + const SpCaptureProcess *pr; + const gchar *cmdline; + + if (type != SP_CAPTURE_FRAME_PROCESS) + { + if (!sp_capture_reader_skip (reader)) + goto failure; + continue; + } + + if (NULL == (pr = sp_capture_reader_read_process (reader))) + goto failure; + + cmdline = g_strdup_printf ("[%s]", pr->cmdline); + g_hash_table_insert (cmdlines, + GINT_TO_POINTER (pr->frame.pid), + (gchar *)sp_callgraph_profile_intern_string (self, cmdline)); + } + + sp_capture_reader_reset (reader); + + /* + * Walk through all of the sample events and resolve instruction-pointers + * to symbol names by loading the particular map and extracting the symbol + * name. If we wanted to support dynamic systems, we'd want to extend this + * to parse information from captured data about the languages jit'd code. + */ + while (sp_capture_reader_peek_type (reader, &type)) + { + SpAddressContext last_context = SP_ADDRESS_CONTEXT_NONE; + const SpCaptureSample *sample; + StackNode *node; + StackNode *iter; + const gchar *cmdline; + guint len = 5; + + if (type != SP_CAPTURE_FRAME_SAMPLE) + { + if (!sp_capture_reader_skip (reader)) + goto failure; + continue; + } + + if (NULL == (sample = sp_capture_reader_read_sample (reader))) + goto failure; + + cmdline = g_hash_table_lookup (cmdlines, GINT_TO_POINTER (sample->frame.pid)); + + node = stack_stash_add_trace (stash, sample->addrs, sample->n_addrs, 1); + + for (iter = node; iter != NULL; iter = iter->parent) + len++; + + if (G_UNLIKELY (resolved->len < len)) + g_array_set_size (resolved, len); + + len = 0; + + for (iter = node; iter != NULL; iter = iter->parent) + { + SpAddressContext context = SP_ADDRESS_CONTEXT_NONE; + SpAddress address = iter->data; + const gchar *symbol = NULL; + + if (sp_address_is_context_switch (address, &context)) + { + if (last_context) + symbol = sp_address_context_to_string (last_context); + else + symbol = NULL; + + last_context = context; + } + else + { + for (j = 0; j < resolvers->len; j++) + { + SpSymbolResolver *resolver = g_ptr_array_index (resolvers, j); + GQuark tag = 0; + gchar *str; + + str = sp_symbol_resolver_resolve (resolver, + sample->frame.time, + sample->frame.pid, + address, + &tag); + + if (str != NULL) + { + symbol = sp_callgraph_profile_intern_string_take (self, str); + if (tag != 0) + g_hash_table_insert (self->tags, (gchar *)symbol, GSIZE_TO_POINTER (tag)); + break; + } + } + } + + if (symbol != NULL) + g_array_index (resolved, SpAddress, len++) = POINTER_TO_U64 (symbol); + } + + if (last_context && last_context != SP_ADDRESS_CONTEXT_USER) + { + /* Kernel threads do not have a user part, so we end up here + * without ever getting a user context. If this happens, + * add the '- - kernel - - ' name, so that kernel threads + * are properly blamed on the kernel + */ + const gchar *name = sp_address_context_to_string (last_context); + g_array_index (resolved, SpAddress, len++) = POINTER_TO_U64 (name); + } + + if (cmdline != NULL) + g_array_index (resolved, guint64, len++) = POINTER_TO_U64 (cmdline); + + g_array_index (resolved, guint64, len++) = POINTER_TO_U64 ("[Everything]"); + + stack_stash_add_trace (resolved_stash, (SpAddress *)resolved->data, len, 1); + } + + ret = TRUE; + +failure: + + if (ret == FALSE) + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Sysprof was unable to generate a callgraph from the system capture.")); + self->stash = resolved_stash; + stack_stash_unref (stash); + g_task_return_boolean (task, ret); +} + +static void +sp_callgraph_profile_generate (SpProfile *profile, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SpCallgraphProfile *self = (SpCallgraphProfile *)profile; + + g_autoptr(GTask) task = NULL; + + g_assert (SP_IS_CALLGRAPH_PROFILE (self)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_task_data (task, self->reader, NULL); + g_task_run_in_thread (task, sp_callgraph_profile_generate_worker); +} + +static gboolean +sp_callgraph_profile_generate_finish (SpProfile *profile, + GAsyncResult *result, + GError **error) +{ + g_assert (SP_IS_CALLGRAPH_PROFILE (profile)); + g_assert (G_IS_TASK (result)); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +profile_iface_init (SpProfileInterface *iface) +{ + iface->generate = sp_callgraph_profile_generate; + iface->generate_finish = sp_callgraph_profile_generate_finish; + iface->set_reader = sp_callgraph_profile_set_reader; +} + +StackStash * +sp_callgraph_profile_get_stash (SpCallgraphProfile *self) +{ + g_return_val_if_fail (SP_IS_CALLGRAPH_PROFILE (self), NULL); + + return self->stash; +} + +GQuark +sp_callgraph_profile_get_tag (SpCallgraphProfile *self, + const gchar *symbol) +{ + g_return_val_if_fail (SP_IS_CALLGRAPH_PROFILE (self), 0); + + return GPOINTER_TO_SIZE (g_hash_table_lookup (self->tags, symbol)); +} diff --git a/lib/sp-callgraph-profile.h b/lib/sp-callgraph-profile.h new file mode 100644 index 00000000..59597bde --- /dev/null +++ b/lib/sp-callgraph-profile.h @@ -0,0 +1,36 @@ +/* sp-callgraph-profile.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_CALLGRAPH_PROFILE_H +#define SP_CALLGRAPH_PROFILE_H + +#include "sp-profile.h" + +G_BEGIN_DECLS + +#define SP_TYPE_CALLGRAPH_PROFILE (sp_callgraph_profile_get_type()) + +G_DECLARE_FINAL_TYPE (SpCallgraphProfile, sp_callgraph_profile, SP, CALLGRAPH_PROFILE, GObject) + +SpProfile *sp_callgraph_profile_new (void); +GQuark sp_callgraph_profile_get_tag (SpCallgraphProfile *self, + const gchar *symbol); + +G_END_DECLS + +#endif /* SP_CALLGRAPH_PROFILE_H */ diff --git a/lib/sp-callgraph-view.c b/lib/sp-callgraph-view.c new file mode 100644 index 00000000..84323b5e --- /dev/null +++ b/lib/sp-callgraph-view.c @@ -0,0 +1,881 @@ +/* sp-callgraph-view.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 . + */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, Red Hat, Inc. + * Copyright 2004, 2005, 2006, Soeren Sandmann + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "sp-callgraph-profile-private.h" +#include "sp-callgraph-view.h" + +#include "util.h" + +typedef struct +{ + SpCallgraphProfile *profile; + + GtkTreeView *callers_view; + GtkTreeView *functions_view; + GtkTreeView *descendants_view; + GtkTreeViewColumn *descendants_name_column; + + guint profile_size; +} SpCallgraphViewPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SpCallgraphView, sp_callgraph_view, GTK_TYPE_BIN) + +enum { + PROP_0, + PROP_PROFILE, + N_PROPS +}; + +enum { + COLUMN_NAME, + COLUMN_SELF, + COLUMN_TOTAL, + COLUMN_POINTER, +}; + + +static void sp_callgraph_view_update_descendants (SpCallgraphView *self, + StackNode *node); + +static GParamSpec *properties [N_PROPS]; + +static guint +sp_callgraph_view_get_profile_size (SpCallgraphView *self) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + StackStash *stash; + StackNode *node; + guint size = 0; + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + + if (priv->profile_size != 0) + return priv->profile_size; + + if (priv->profile == NULL) + return 0; + + if (NULL == (stash = sp_callgraph_profile_get_stash (priv->profile))) + return 0; + + for (node = stack_stash_get_root (stash); node != NULL; node = node->siblings) + size += node->total; + + priv->profile_size = size; + + return size; +} + +static void +build_functions_store (StackNode *node, + gpointer user_data) +{ + struct { + GtkListStore *store; + gdouble profile_size; + } *state = user_data; + GtkTreeIter iter; + const StackNode *n; + guint size = 0; + guint total = 0; + + g_assert (state != NULL); + g_assert (GTK_IS_LIST_STORE (state->store)); + + for (n = node; n != NULL; n = n->next) + { + size += n->size; + if (n->toplevel) + total += n->total; + } + + gtk_list_store_append (state->store, &iter); + gtk_list_store_set (state->store, &iter, + COLUMN_NAME, (const gchar *)node->data, + COLUMN_SELF, 100.0 * size / state->profile_size, + COLUMN_TOTAL, 100.0 * total / state->profile_size, + COLUMN_POINTER, node, + -1); + +} + +static void +sp_callgraph_view_load (SpCallgraphView *self, + SpCallgraphProfile *profile) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + GtkListStore *functions; + StackStash *stash; + StackNode *n; + GtkTreeIter iter; + struct { + GtkListStore *store; + gdouble profile_size; + } state = { 0 }; + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + g_assert (SP_IS_CALLGRAPH_PROFILE (profile)); + + /* + * TODO: This is probably the type of thing we want to do off the main + * thread. We should be able to build the tree models off thread + * and then simply apply them on the main thread. + * + * In the mean time, we should set the state of the widget to + * insensitive and give some indication of loading progress. + */ + + g_set_object (&priv->profile, profile); + + if (NULL == (stash = sp_callgraph_profile_get_stash (profile))) + return; + + for (n = stack_stash_get_root (stash); n; n = n->siblings) + state.profile_size += n->total; + + functions = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER); + + state.store = functions; + stack_stash_foreach_by_address (stash, build_functions_store, &state); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (functions), + COLUMN_TOTAL, + GTK_SORT_DESCENDING); + + gtk_tree_view_set_model (priv->functions_view, GTK_TREE_MODEL (functions)); + gtk_tree_view_set_model (priv->callers_view, NULL); + gtk_tree_view_set_model (priv->descendants_view, NULL); + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (functions), &iter)) + { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (priv->functions_view); + gtk_tree_selection_select_iter (selection, &iter); + } + + g_clear_object (&functions); +} + +static void +sp_callgraph_view_unload (SpCallgraphView *self) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + g_assert (SP_IS_CALLGRAPH_PROFILE (priv->profile)); + + g_clear_object (&priv->profile); + priv->profile_size = 0; +} + +void +sp_callgraph_view_set_profile (SpCallgraphView *self, + SpCallgraphProfile *profile) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + + g_return_if_fail (SP_IS_CALLGRAPH_VIEW (self)); + g_return_if_fail (!profile || SP_IS_CALLGRAPH_PROFILE (profile)); + + if (profile != priv->profile) + { + if (priv->profile) + sp_callgraph_view_unload (self); + + if (profile) + sp_callgraph_view_load (self, profile); + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROFILE]); + } +} + +static void +sp_callgraph_view_expand_descendants (SpCallgraphView *self) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + GtkTreeModel *model; + GList *all_paths = NULL; + GtkTreePath *first_path; + GtkTreeIter iter; + gdouble top_value = 0; + gint max_rows = 40; /* FIXME */ + gint n_rows; + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + + model = gtk_tree_view_get_model (priv->descendants_view); + first_path = gtk_tree_path_new_first (); + all_paths = g_list_prepend (all_paths, first_path); + n_rows = 1; + + gtk_tree_model_get_iter (model, &iter, first_path); + gtk_tree_model_get (model, &iter, + COLUMN_TOTAL, &top_value, + -1); + + while ((all_paths != NULL) && (n_rows < max_rows)) + { + GtkTreeIter best_iter; + GtkTreePath *best_path = NULL; + GList *list; + gdouble best_value = 0.0; + gint n_children; + gint i; + + for (list = all_paths; list != NULL; list = list->next) + { + GtkTreePath *path = list->data; + + g_assert (path != NULL); + + if (gtk_tree_model_get_iter (model, &iter, path)) + { + gdouble value; + + gtk_tree_model_get (model, &iter, + COLUMN_TOTAL, &value, + -1); + + if (value >= best_value) + { + best_value = value; + best_path = path; + best_iter = iter; + } + } + } + + n_children = gtk_tree_model_iter_n_children (model, &best_iter); + + if ((n_children > 0) && + ((best_value / top_value) > 0.04) && + ((n_children + gtk_tree_path_get_depth (best_path)) / (gdouble)max_rows) < (best_value / top_value)) + { + gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE); + n_rows += n_children; + + if (gtk_tree_path_get_depth (best_path) < 4) + { + GtkTreePath *path; + + path = gtk_tree_path_copy (best_path); + gtk_tree_path_down (path); + + for (i = 0; i < n_children; i++) + { + all_paths = g_list_prepend (all_paths, path); + + path = gtk_tree_path_copy (path); + gtk_tree_path_next (path); + } + + gtk_tree_path_free (path); + } + } + + all_paths = g_list_remove (all_paths, best_path); + + /* Always expand at least once */ + if ((all_paths == NULL) && (n_rows == 1)) + gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE); + + gtk_tree_path_free (best_path); + } + + g_list_free_full (all_paths, (GDestroyNotify)gtk_tree_path_free); +} + +typedef struct +{ + StackNode *node; + const gchar *name; + guint self; + guint total; +} Caller; + +static Caller * +caller_new (StackNode *node) +{ + Caller *c; + + c = g_slice_new (Caller); + c->name = U64_TO_POINTER (node->data); + c->self = 0; + c->total = 0; + c->node = node; + + return c; +} + +static void +caller_free (gpointer data) +{ + Caller *c = data; + g_slice_free (Caller, c); +} + +static void +sp_callgraph_view_function_selection_changed (SpCallgraphView *self, + GtkTreeSelection *selection) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + GtkTreeModel *model = NULL; + GtkTreeIter iter; + GtkListStore *callers_store; + g_autoptr(GHashTable) callers = NULL; + g_autoptr(GHashTable) processed = NULL; + StackNode *callees = NULL; + StackNode *node; + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + g_assert (GTK_IS_TREE_SELECTION (selection)); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_view_set_model (priv->callers_view, NULL); + gtk_tree_view_set_model (priv->descendants_view, NULL); + return; + } + + gtk_tree_model_get (model, &iter, + COLUMN_POINTER, &callees, + -1); + + sp_callgraph_view_update_descendants (self, callees); + + callers_store = gtk_list_store_new (4, + G_TYPE_STRING, + G_TYPE_DOUBLE, + G_TYPE_DOUBLE, + G_TYPE_POINTER); + + callers = g_hash_table_new_full (NULL, NULL, NULL, caller_free); + processed = g_hash_table_new (NULL, NULL); + + for (node = callees; node != NULL; node = node->next) + { + Caller *c; + + if (!node->parent) + continue; + + c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data)); + + if (c == NULL) + { + c = caller_new (node->parent); + g_hash_table_insert (callers, (gpointer)c->name, c); + } + } + + for (node = callees; node != NULL; node = node->next) + { + StackNode *top_caller = node->parent; + StackNode *top_callee = node; + StackNode *n; + Caller *c; + + if (!node->parent) + continue; + + /* + * We could have a situation where the function was called in a + * reentrant fashion, so we want to take the top-most match in the + * stack. + */ + for (n = node; n && n->parent; n = n->parent) + { + if (n->data == node->data && n->parent->data == node->parent->data) + { + top_caller = n->parent; + top_callee = n; + } + } + + c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data)); + + g_assert (c != NULL); + + if (!g_hash_table_lookup (processed, top_caller)) + { + c->total += top_callee->total; + g_hash_table_insert (processed, top_caller, top_caller); + } + + c->self += node->size; + } + + { + GHashTableIter hiter; + gpointer key, value; + guint size = 0; + + size = sp_callgraph_view_get_profile_size (self); + + g_hash_table_iter_init (&hiter, callers); + + while (g_hash_table_iter_next (&hiter, &key, &value)) + { + Caller *c = value; + + gtk_list_store_append (callers_store, &iter); + gtk_list_store_set (callers_store, &iter, + COLUMN_NAME, c->name, + COLUMN_SELF, c->self * 100.0 / size, + COLUMN_TOTAL, c->total * 100.0 / size, + COLUMN_POINTER, c->node, + -1); + } + } + + gtk_tree_view_set_model (priv->callers_view, GTK_TREE_MODEL (callers_store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (callers_store), + COLUMN_TOTAL, + GTK_SORT_DESCENDING); + + g_clear_object (&callers_store); +} + +static void +sp_callgraph_view_set_node (SpCallgraphView *self, + StackNode *node) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + GtkTreeModel *model; + GtkTreeIter iter; + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + g_assert (node != NULL); + + model = gtk_tree_view_get_model (priv->functions_view); + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + StackNode *item = NULL; + + gtk_tree_model_get (model, &iter, + COLUMN_POINTER, &item, + -1); + + if (item == node) + { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (priv->functions_view); + gtk_tree_selection_select_iter (selection, &iter); + + break; + } + } + while (gtk_tree_model_iter_next (model, &iter)); + } +} + +static void +sp_callgraph_view_descendant_activated (SpCallgraphView *self, + GtkTreePath *path, + GtkTreeViewColumn *column, + GtkTreeView *tree_view) +{ + GtkTreeModel *model; + StackNode *node = NULL; + GtkTreeIter iter; + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + g_assert (GTK_IS_TREE_VIEW (tree_view)); + g_assert (path != NULL); + g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); + + model = gtk_tree_view_get_model (tree_view); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + return; + + gtk_tree_model_get (model, &iter, + COLUMN_POINTER, &node, + -1); + + if (node != NULL) + sp_callgraph_view_set_node (self, node); +} + +static void +sp_callgraph_view_caller_activated (SpCallgraphView *self, + GtkTreePath *path, + GtkTreeViewColumn *column, + GtkTreeView *tree_view) +{ + GtkTreeModel *model; + StackNode *node = NULL; + GtkTreeIter iter; + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + g_assert (GTK_IS_TREE_VIEW (tree_view)); + g_assert (path != NULL); + g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); + + model = gtk_tree_view_get_model (tree_view); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + return; + + gtk_tree_model_get (model, &iter, + COLUMN_POINTER, &node, + -1); + + if (node != NULL) + sp_callgraph_view_set_node (self, node); +} + +static void +sp_callgraph_view_tag_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + SpCallgraphView *self = data; + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + StackNode *node = NULL; + const gchar *str = NULL; + + gtk_tree_model_get (model, iter, COLUMN_POINTER, &node, -1); + + if (node && node->data) + { + GQuark tag; + + tag = sp_callgraph_profile_get_tag (priv->profile, GSIZE_TO_POINTER (node->data)); + if (tag != 0) + str = g_quark_to_string (tag); + } + + g_object_set (cell, "text", str, NULL); +} + +static void +sp_callgraph_view_finalize (GObject *object) +{ + SpCallgraphView *self = (SpCallgraphView *)object; + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + + g_clear_object (&priv->profile); + + G_OBJECT_CLASS (sp_callgraph_view_parent_class)->finalize (object); +} + +static void +sp_callgraph_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpCallgraphView *self = SP_CALLGRAPH_VIEW (object); + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + + switch (prop_id) + { + case PROP_PROFILE: + g_value_set_object (value, priv->profile); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_callgraph_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpCallgraphView *self = SP_CALLGRAPH_VIEW (object); + + switch (prop_id) + { + case PROP_PROFILE: + sp_callgraph_view_set_profile (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_callgraph_view_class_init (SpCallgraphViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = sp_callgraph_view_finalize; + object_class->get_property = sp_callgraph_view_get_property; + object_class->set_property = sp_callgraph_view_set_property; + + properties [PROP_PROFILE] = + g_param_spec_object ("profile", + "Profile", + "The callgraph profile to view", + SP_TYPE_CALLGRAPH_PROFILE, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/sysprof/ui/sp-callgraph-view.ui"); + + gtk_widget_class_bind_template_child_private (widget_class, SpCallgraphView, callers_view); + gtk_widget_class_bind_template_child_private (widget_class, SpCallgraphView, functions_view); + gtk_widget_class_bind_template_child_private (widget_class, SpCallgraphView, descendants_view); + gtk_widget_class_bind_template_child_private (widget_class, SpCallgraphView, descendants_name_column); +} + +static void +sp_callgraph_view_init (SpCallgraphView *self) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + GtkTreeSelection *selection; + GtkCellRenderer *cell; + + gtk_widget_init_template (GTK_WIDGET (self)); + + selection = gtk_tree_view_get_selection (priv->functions_view); + + g_signal_connect_object (selection, + "changed", + G_CALLBACK (sp_callgraph_view_function_selection_changed), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->descendants_view, + "row-activated", + G_CALLBACK (sp_callgraph_view_descendant_activated), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->callers_view, + "row-activated", + G_CALLBACK (sp_callgraph_view_caller_activated), + self, + G_CONNECT_SWAPPED); + + cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, + "foreground", "#666666", + "scale", PANGO_SCALE_SMALL, + "xalign", 1.0f, + NULL); + gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, FALSE); + gtk_tree_view_column_set_cell_data_func (priv->descendants_name_column, cell, + sp_callgraph_view_tag_data_func, + self, NULL); +} + +typedef struct _Descendant Descendant; + +struct _Descendant +{ + const gchar *name; + guint self; + guint cumulative; + Descendant *parent; + Descendant *siblings; + Descendant *children; +}; + +static void +build_tree_cb (StackLink *trace, + gint size, + gpointer user_data) +{ + Descendant **tree = user_data; + Descendant *parent = NULL; + StackLink *link; + + g_assert (trace != NULL); + g_assert (tree != NULL); + + /* Get last item */ + link = trace; + while (link->next) + link = link->next; + + for (; link != NULL; link = link->prev) + { + const gchar *address = U64_TO_POINTER (link->data); + Descendant *prev = NULL; + Descendant *match = NULL; + + for (match = *tree; match != NULL; match = match->siblings) + { + if (match->name == address) + { + if (prev != NULL) + { + /* Move to front */ + prev->siblings = match->siblings; + match->siblings = *tree; + *tree = match; + } + break; + } + } + + if (match == NULL) + { + /* Have we seen this object further up the tree? */ + for (match = parent; match != NULL; match = match->parent) + { + if (match->name == address) + break; + } + } + + if (match == NULL) + { + match = g_slice_new (Descendant); + match->name = address; + match->cumulative = 0; + match->self = 0; + match->children = NULL; + match->parent = parent; + match->siblings = *tree; + *tree = match; + } + + tree = &match->children; + parent = match; + } + + parent->self += size; + + for (; parent != NULL; parent = parent->parent) + parent->cumulative += size; +} + +static Descendant * +build_tree (StackNode *node) +{ + Descendant *tree = NULL; + + for (; node != NULL; node = node->next) + { + if (node->toplevel) + stack_node_foreach_trace (node, build_tree_cb, &tree); + } + + return tree; +} + +static void +append_to_tree_and_free (SpCallgraphView *self, + StackStash *stash, + GtkTreeStore *store, + Descendant *item, + GtkTreeIter *parent) +{ + StackNode *node = NULL; + GtkTreeIter iter; + guint profile_size; + + g_assert (GTK_IS_TREE_STORE (store)); + g_assert (item != NULL); + + profile_size = sp_callgraph_view_get_profile_size (self); + + gtk_tree_store_append (store, &iter, parent); + + node = stack_stash_find_node (stash, (gpointer)item->name); + + gtk_tree_store_set (store, &iter, + COLUMN_NAME, item->name, + COLUMN_SELF, item->self * 100.0 / (gdouble)profile_size, + COLUMN_TOTAL, item->cumulative * 100.0 / (gdouble)profile_size, + COLUMN_POINTER, node, + -1); + + if (item->siblings != NULL) + append_to_tree_and_free (self, stash, store, item->siblings, parent); + + if (item->children != NULL) + append_to_tree_and_free (self, stash, store, item->children, &iter); + + g_slice_free (Descendant, item); +} + +static void +sp_callgraph_view_update_descendants (SpCallgraphView *self, + StackNode *node) +{ + SpCallgraphViewPrivate *priv = sp_callgraph_view_get_instance_private (self); + GtkTreeStore *store; + + g_assert (SP_IS_CALLGRAPH_VIEW (self)); + + store = gtk_tree_store_new (4, + G_TYPE_STRING, + G_TYPE_DOUBLE, + G_TYPE_DOUBLE, + G_TYPE_POINTER); + + if (priv->profile != NULL) + { + StackStash *stash; + + stash = sp_callgraph_profile_get_stash (priv->profile); + if (stash != NULL) + { + Descendant *tree; + + tree = build_tree (node); + if (tree != NULL) + append_to_tree_and_free (self, stash, store, tree, NULL); + } + } + + gtk_tree_view_set_model (priv->descendants_view, GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + COLUMN_TOTAL, GTK_SORT_DESCENDING); + sp_callgraph_view_expand_descendants (self); + + g_clear_object (&store); +} diff --git a/lib/sp-callgraph-view.h b/lib/sp-callgraph-view.h new file mode 100644 index 00000000..8e9898e7 --- /dev/null +++ b/lib/sp-callgraph-view.h @@ -0,0 +1,44 @@ +/* sp-callgraph-view.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_CALLGRAPH_VIEW_H +#define SP_CALLGRAPH_VIEW_H + +#include + +#include "sp-callgraph-profile.h" + +G_BEGIN_DECLS + +#define SP_TYPE_CALLGRAPH_VIEW (sp_callgraph_view_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpCallgraphView, sp_callgraph_view, SP, CALLGRAPH_VIEW, GtkBin) + +struct _SpCallgraphViewClass +{ + GtkBinClass parent_class; +}; + +GtkWidget *sp_callgraph_view_new (void); +SpCallgraphProfile *sp_callgraph_view_get_profile (SpCallgraphView *self); +void sp_callgraph_view_set_profile (SpCallgraphView *self, + SpCallgraphProfile *profile); + +G_END_DECLS + +#endif /* SP_CALLGRAPH_VIEW_H */ diff --git a/lib/sp-capture-reader.c b/lib/sp-capture-reader.c new file mode 100644 index 00000000..f5d4544f --- /dev/null +++ b/lib/sp-capture-reader.c @@ -0,0 +1,686 @@ +/* sp-capture-reader.c + * + * Copyright (C) 2016 Christian Hergert + * + * This file 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 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sp-capture-reader.h" +#include "sp-capture-writer.h" + +struct _SpCaptureReader +{ + volatile gint ref_count; + gchar *filename; + guint8 *buf; + gsize bufsz; + gsize len; + gsize pos; + gsize fd_off; + int fd; + gint endian; + SpCaptureFileHeader header; +}; + +#ifndef SP_DISABLE_GOBJECT +G_DEFINE_BOXED_TYPE (SpCaptureReader, sp_capture_reader, + sp_capture_reader_ref, sp_capture_reader_unref) +#endif + +static gboolean +sp_capture_reader_read_file_header (SpCaptureReader *self, + SpCaptureFileHeader *header, + GError **error) +{ + g_assert (self != NULL); + g_assert (header != NULL); + + if (sizeof *header != pread (self->fd, header, sizeof *header, 0L)) + { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + return FALSE; + } + + if (header->magic != SP_CAPTURE_MAGIC) + { + g_set_error (error, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "Capture file magic does not match"); + return FALSE; + } + + header->capture_time[sizeof header->capture_time - 1] = '\0'; + + return TRUE; +} + +static void +sp_capture_reader_finalize (SpCaptureReader *self) +{ + if (self != NULL) + { + close (self->fd); + g_free (self->buf); + g_free (self->filename); + g_free (self); + } +} + +const gchar * +sp_capture_reader_get_time (SpCaptureReader *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return self->header.capture_time; +} + +const gchar * +sp_capture_reader_get_filename (SpCaptureReader *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return self->filename; +} + +SpCaptureReader * +sp_capture_reader_new_from_fd (int fd, + GError **error) +{ + SpCaptureReader *self; + + g_assert (fd > -1); + + self = g_new0 (SpCaptureReader, 1); + self->ref_count = 1; + self->bufsz = G_MAXUSHORT * 2; + self->buf = g_malloc (self->bufsz); + self->len = 0; + self->pos = 0; + self->fd = fd; + self->fd_off = sizeof (SpCaptureFileHeader); + + if (!sp_capture_reader_read_file_header (self, &self->header, error)) + { + sp_capture_reader_finalize (self); + return NULL; + } + + if (self->header.little_endian) + self->endian = G_LITTLE_ENDIAN; + else + self->endian = G_BIG_ENDIAN; + + return self; +} + +SpCaptureReader * +sp_capture_reader_new (const gchar *filename, + GError **error) +{ + SpCaptureReader *self; + int fd; + + g_assert (filename != NULL); + + if (-1 == (fd = open (filename, O_RDONLY, 0))) + { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + return NULL; + } + + if (NULL == (self = sp_capture_reader_new_from_fd (fd, error))) + { + close (fd); + return NULL; + } + + self->filename = g_strdup (filename); + + return self; +} + +static inline void +sp_capture_reader_bswap_frame (SpCaptureReader *self, + SpCaptureFrame *frame) +{ + g_assert (self != NULL); + g_assert (frame!= NULL); + + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) + { + frame->len = GUINT16_SWAP_LE_BE (frame->len); + frame->cpu = GUINT16_SWAP_LE_BE (frame->len); + frame->pid = GUINT32_SWAP_LE_BE (frame->len); + frame->time = GUINT64_SWAP_LE_BE (frame->len); + } +} + +static inline void +sp_capture_reader_bswap_map (SpCaptureReader *self, + SpCaptureMap *map) +{ + g_assert (self != NULL); + g_assert (map != NULL); + + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) + { + map->start = GUINT64_SWAP_LE_BE (map->start); + map->end = GUINT64_SWAP_LE_BE (map->end); + map->offset = GUINT64_SWAP_LE_BE (map->offset); + map->inode = GUINT64_SWAP_LE_BE (map->inode); + } +} + +static inline void +sp_capture_reader_bswap_jitmap (SpCaptureReader *self, + SpCaptureJitmap *jitmap) +{ + g_assert (self != NULL); + g_assert (jitmap != NULL); + + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) + jitmap->n_jitmaps = GUINT64_SWAP_LE_BE (jitmap->n_jitmaps); +} + +static gboolean +sp_capture_reader_ensure_space_for (SpCaptureReader *self, + gsize len) +{ + g_assert (self != NULL); + g_assert (len > 0); + + if ((self->len - self->pos) < len) + { + gssize r; + + g_assert (self->len >= self->pos); + + memmove (self->buf, &self->buf[self->pos], self->len - self->pos); + self->len -= self->pos; + self->pos = 0; + + while ((self->len - self->pos) <= len) + { + g_assert (self->pos + self->len < self->bufsz); + + /* Read into our buffer after our current read position */ + r = pread (self->fd, + &self->buf[self->len], + self->bufsz - self->len, + self->fd_off); + + if (r <= 0) + break; + + self->fd_off += r; + self->len += r; + } + } + + return (self->len - self->pos) >= len; +} + +gboolean +sp_capture_reader_skip (SpCaptureReader *self) +{ + SpCaptureFrame *frame; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + if (!sp_capture_reader_ensure_space_for (self, sizeof (SpCaptureFrame))) + return FALSE; + + frame = (SpCaptureFrame *)(gpointer)&self->buf[self->pos]; + sp_capture_reader_bswap_frame (self, frame); + + if (frame->len < sizeof (SpCaptureFrame)) + return FALSE; + + if (!sp_capture_reader_ensure_space_for (self, frame->len)) + return FALSE; + + frame = (SpCaptureFrame *)(gpointer)&self->buf[self->pos]; + + self->pos += frame->len; + + if ((self->pos % SP_CAPTURE_ALIGN) != 0) + return FALSE; + + return TRUE; +} + +gboolean +sp_capture_reader_peek_type (SpCaptureReader *self, + SpCaptureFrameType *type) +{ + SpCaptureFrame *frame; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + g_assert (type != NULL); + + *type = 0; + + if (!sp_capture_reader_ensure_space_for (self, sizeof *frame)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + frame = (SpCaptureFrame *)(gpointer)&self->buf[self->pos]; + + *type = frame->type; + + return TRUE; +} + +static const SpCaptureFrame * +sp_capture_reader_read_basic (SpCaptureReader *self, + SpCaptureFrameType type, + gsize extra) +{ + SpCaptureFrame *frame; + gsize len = sizeof *frame + extra; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + + if (!sp_capture_reader_ensure_space_for (self, len)) + return NULL; + + frame = (SpCaptureFrame *)(gpointer)&self->buf[self->pos]; + + sp_capture_reader_bswap_frame (self, frame); + + if (frame->len < len) + return NULL; + + if (frame->type != type) + return NULL; + + self->pos += frame->len; + + return frame; +} + +const SpCaptureTimestamp * +sp_capture_reader_read_timestamp (SpCaptureReader *self) +{ + return (SpCaptureTimestamp *) + sp_capture_reader_read_basic (self, SP_CAPTURE_FRAME_TIMESTAMP, 0); +} + +const SpCaptureExit * +sp_capture_reader_read_exit (SpCaptureReader *self) +{ + return (SpCaptureExit *) + sp_capture_reader_read_basic (self, SP_CAPTURE_FRAME_EXIT, 0); +} + +const SpCaptureFork * +sp_capture_reader_read_fork (SpCaptureReader *self) +{ + SpCaptureFork *fk; + + g_assert (self != NULL); + + fk = (SpCaptureFork *) + sp_capture_reader_read_basic (self, SP_CAPTURE_FRAME_FORK, sizeof(guint32)); + + if (fk != NULL) + { + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) + fk->child_pid = GUINT32_SWAP_LE_BE (fk->child_pid); + } + + return fk; +} + +const SpCaptureMap * +sp_capture_reader_read_map (SpCaptureReader *self) +{ + SpCaptureMap *map; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + + if (!sp_capture_reader_ensure_space_for (self, sizeof *map)) + return NULL; + + map = (SpCaptureMap *)(gpointer)&self->buf[self->pos]; + + sp_capture_reader_bswap_frame (self, &map->frame); + + if (map->frame.type != SP_CAPTURE_FRAME_MAP) + return NULL; + + if (map->frame.len < (sizeof *map + 1)) + return NULL; + + if (!sp_capture_reader_ensure_space_for (self, map->frame.len)) + return NULL; + + map = (SpCaptureMap *)(gpointer)&self->buf[self->pos]; + + if (self->buf[self->pos + map->frame.len - 1] != '\0') + return NULL; + + sp_capture_reader_bswap_map (self, map); + + self->pos += map->frame.len; + + if ((self->pos % SP_CAPTURE_ALIGN) != 0) + return NULL; + + return map; +} + +const SpCaptureProcess * +sp_capture_reader_read_process (SpCaptureReader *self) +{ + SpCaptureProcess *process; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + + if (!sp_capture_reader_ensure_space_for (self, sizeof *process)) + return NULL; + + process = (SpCaptureProcess *)(gpointer)&self->buf[self->pos]; + + sp_capture_reader_bswap_frame (self, &process->frame); + + if (process->frame.type != SP_CAPTURE_FRAME_PROCESS) + return NULL; + + if (process->frame.len < (sizeof *process + 1)) + return NULL; + + if (!sp_capture_reader_ensure_space_for (self, process->frame.len)) + return NULL; + + process = (SpCaptureProcess *)(gpointer)&self->buf[self->pos]; + + if (self->buf[self->pos + process->frame.len - 1] != '\0') + return NULL; + + self->pos += process->frame.len; + + if ((self->pos % SP_CAPTURE_ALIGN) != 0) + return NULL; + + return process; +} + +GHashTable * +sp_capture_reader_read_jitmap (SpCaptureReader *self) +{ + g_autoptr(GHashTable) ret = NULL; + SpCaptureJitmap *jitmap; + guint8 *buf; + guint8 *endptr; + guint i; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + + if (!sp_capture_reader_ensure_space_for (self, sizeof *jitmap)) + return NULL; + + jitmap = (SpCaptureJitmap *)(gpointer)&self->buf[self->pos]; + + sp_capture_reader_bswap_frame (self, &jitmap->frame); + + if (jitmap->frame.type != SP_CAPTURE_FRAME_JITMAP) + return NULL; + + if (jitmap->frame.len < sizeof *jitmap) + return NULL; + + if (!sp_capture_reader_ensure_space_for (self, jitmap->frame.len)) + return NULL; + + jitmap = (SpCaptureJitmap *)(gpointer)&self->buf[self->pos]; + + ret = g_hash_table_new_full (NULL, NULL, NULL, g_free); + + buf = jitmap->data; + endptr = &self->buf[self->pos + jitmap->frame.len]; + + for (i = 0; i < jitmap->n_jitmaps; i++) + { + SpCaptureAddress addr; + const gchar *str; + + if (buf + sizeof addr >= endptr) + return NULL; + + memcpy (&addr, buf, sizeof addr); + buf += sizeof addr; + + str = (gchar *)buf; + + buf = memchr (buf, '\0', (endptr - buf)); + + if (buf == NULL) + return NULL; + + buf++; + + g_hash_table_insert (ret, GSIZE_TO_POINTER (addr), g_strdup (str)); + } + + self->pos += jitmap->frame.len; + + return g_steal_pointer (&ret); +} + +const SpCaptureSample * +sp_capture_reader_read_sample (SpCaptureReader *self) +{ + SpCaptureSample *sample; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + + if (!sp_capture_reader_ensure_space_for (self, sizeof *sample)) + return NULL; + + sample = (SpCaptureSample *)(gpointer)&self->buf[self->pos]; + + sp_capture_reader_bswap_frame (self, &sample->frame); + + if (sample->frame.type != SP_CAPTURE_FRAME_SAMPLE) + return NULL; + + if (sample->frame.len < sizeof *sample) + return NULL; + + if (self->endian != G_BYTE_ORDER) + sample->n_addrs = GUINT16_SWAP_LE_BE (sample->n_addrs); + + if (sample->frame.len < (sizeof *sample + (sizeof(SpCaptureAddress) * sample->n_addrs))) + return NULL; + + if (!sp_capture_reader_ensure_space_for (self, sample->frame.len)) + return NULL; + + sample = (SpCaptureSample *)(gpointer)&self->buf[self->pos]; + + if (self->endian != G_BYTE_ORDER) + { + guint i; + + for (i = 0; i < sample->n_addrs; i++) + sample->addrs[i] = GUINT64_SWAP_LE_BE (sample->addrs[i]); + } + + self->pos += sample->frame.len; + + return sample; +} + +gboolean +sp_capture_reader_reset (SpCaptureReader *self) +{ + g_assert (self != NULL); + + self->fd_off = sizeof (SpCaptureFileHeader); + self->pos = 0; + self->len = 0; + + return TRUE; +} + +SpCaptureReader * +sp_capture_reader_ref (SpCaptureReader *self) +{ + g_assert (self != NULL); + g_assert (self->ref_count > 0); + + g_atomic_int_inc (&self->ref_count); + + return self; +} + +void +sp_capture_reader_unref (SpCaptureReader *self) +{ + g_assert (self != NULL); + g_assert (self->ref_count > 0); + + if (g_atomic_int_dec_and_test (&self->ref_count)) + sp_capture_reader_finalize (self); +} + +gboolean +sp_capture_reader_splice (SpCaptureReader *self, + SpCaptureWriter *dest, + GError **error) +{ + g_assert (self != NULL); + g_assert (self->fd != -1); + g_assert (dest != NULL); + + /* Flush before writing anything to ensure consistency */ + if (!sp_capture_writer_flush (dest)) + { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + return FALSE; + } + + /* + * We don't need to track position because writer will + * track the current position to avoid reseting it. + */ + + /* Perform the splice */ + return _sp_capture_writer_splice_from_fd (dest, self->fd, error); +} + +/** + * sp_capture_reader_save_as: + * @self: An #SpCaptureReader + * @filename: the file to save the capture as + * @error: a location for a #GError or %NULL. + * + * This is a convenience function for copying a capture file for which + * you may have already discarded the writer for. + * + * Returns: %TRUE on success; otherwise %FALSE and @error is set. + */ +gboolean +sp_capture_reader_save_as (SpCaptureReader *self, + const gchar *filename, + GError **error) +{ + struct stat stbuf; + off_t in_off; + gsize to_write; + int fd = -1; + + g_assert (self != NULL); + g_assert (filename != NULL); + + if (-1 == (fd = open (filename, O_CREAT | O_WRONLY, 0640))) + goto handle_errno; + + if (-1 == fstat (self->fd, &stbuf)) + goto handle_errno; + + if (-1 == ftruncate (fd, stbuf.st_size)) + goto handle_errno; + + if ((off_t)-1 == lseek (fd, 0L, SEEK_SET)) + goto handle_errno; + + in_off = 0; + to_write = stbuf.st_size; + + while (to_write > 0) + { + gssize written; + + written = sendfile (fd, self->fd, &in_off, to_write); + + if (written < 0) + goto handle_errno; + + if (written == 0 && errno != EAGAIN) + goto handle_errno; + + g_assert (written <= (gssize)to_write); + + to_write -= written; + } + + close (fd); + + return TRUE; + +handle_errno: + if (fd != -1) + close (fd); + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + + return FALSE; +} diff --git a/lib/sp-capture-reader.h b/lib/sp-capture-reader.h new file mode 100644 index 00000000..14d93c85 --- /dev/null +++ b/lib/sp-capture-reader.h @@ -0,0 +1,65 @@ +/* sp-capture-reader.h + * + * Copyright (C) 2016 Christian Hergert + * + * This file 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 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License + * along with this program. If not, see . + */ + +#ifndef SP_CAPTURE_READER_H +#define SP_CAPTURE_READER_H + +#include "sp-capture-types.h" + +G_BEGIN_DECLS + +typedef struct _SpCaptureReader SpCaptureReader; + +SpCaptureReader *sp_capture_reader_new (const gchar *filename, + GError **error); +SpCaptureReader *sp_capture_reader_new_from_fd (int fd, + GError **error); +SpCaptureReader *sp_capture_reader_ref (SpCaptureReader *self); +void sp_capture_reader_unref (SpCaptureReader *self); +const gchar *sp_capture_reader_get_filename (SpCaptureReader *self); +const gchar *sp_capture_reader_get_time (SpCaptureReader *self); +gboolean sp_capture_reader_skip (SpCaptureReader *self); +gboolean sp_capture_reader_peek_type (SpCaptureReader *self, + SpCaptureFrameType *type); +const SpCaptureMap *sp_capture_reader_read_map (SpCaptureReader *self); +const SpCaptureExit *sp_capture_reader_read_exit (SpCaptureReader *self); +const SpCaptureFork *sp_capture_reader_read_fork (SpCaptureReader *self); +const SpCaptureTimestamp *sp_capture_reader_read_timestamp (SpCaptureReader *self); +const SpCaptureProcess *sp_capture_reader_read_process (SpCaptureReader *self); +const SpCaptureSample *sp_capture_reader_read_sample (SpCaptureReader *self); +GHashTable *sp_capture_reader_read_jitmap (SpCaptureReader *self); +gboolean sp_capture_reader_reset (SpCaptureReader *self); +gboolean sp_capture_reader_splice (SpCaptureReader *self, + SpCaptureWriter *dest, + GError **error); +gboolean sp_capture_reader_save_as (SpCaptureReader *self, + const gchar *filename, + GError **error); + +#ifndef SP_DISABLE_GOBJECT +# define SP_TYPE_CAPTURE_READER (sp_capture_reader_get_type()) + GType sp_capture_reader_get_type (void); +#endif + +#if GLIB_CHECK_VERSION(2, 44, 0) + G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpCaptureReader, sp_capture_reader_unref) +#endif + +G_END_DECLS + +#endif /* SP_CAPTURE_READER_H */ diff --git a/lib/sp-capture-types.h b/lib/sp-capture-types.h new file mode 100644 index 00000000..7bb3ab76 --- /dev/null +++ b/lib/sp-capture-types.h @@ -0,0 +1,155 @@ +/* sp-capture-types.h + * + * Copyright (C) 2016 Christian Hergert + * + * This file 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 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License + * along with this program. If not, see . + */ + +#ifndef SP_CAPTURE_FORMAT_H +#define SP_CAPTURE_FORMAT_H + +#include + +#ifndef SP_DISABLE_GOBJECT +# include +#endif + +G_BEGIN_DECLS + +#define SP_CAPTURE_MAGIC (GUINT32_TO_LE(0xFDCA975E)) +#define SP_CAPTURE_ALIGN (sizeof(SpCaptureAddress)) + +#if __WORDSIZE == 64 +# define SP_CAPTURE_JITMAP_MARK G_GUINT64_CONSTANT(0xE000000000000000) +# define SP_CAPTURE_ADDRESS_FORMAT "0x%016lx" +#else +# define SP_CAPTURE_JITMAP_MARK G_GUINT64_CONSTANT(0xE0000000) +# define SP_CAPTURE_ADDRESS_FORMAT "0x%016llx" +#endif + +#define SP_CAPTURE_CURRENT_TIME (g_get_monotonic_time() * 1000L) + +typedef struct _SpCaptureReader SpCaptureReader; +typedef struct _SpCaptureWriter SpCaptureWriter; + +typedef guint64 SpCaptureAddress; + +typedef enum +{ + SP_CAPTURE_FRAME_TIMESTAMP = 1, + SP_CAPTURE_FRAME_SAMPLE = 2, + SP_CAPTURE_FRAME_MAP = 3, + SP_CAPTURE_FRAME_PROCESS = 4, + SP_CAPTURE_FRAME_FORK = 5, + SP_CAPTURE_FRAME_EXIT = 6, + SP_CAPTURE_FRAME_JITMAP = 7, +} SpCaptureFrameType; + +#pragma pack(push, 1) + +typedef struct +{ + guint32 magic; + guint8 version; + guint32 little_endian : 1; + guint32 padding : 23; + gchar capture_time[64]; + gchar suffix[184]; +} SpCaptureFileHeader; + +typedef struct +{ + guint16 len; + gint16 cpu; + gint32 pid; + gint64 time; + guint8 type; + guint64 padding : 56; + guint8 data[0]; +} SpCaptureFrame; + +typedef struct +{ + SpCaptureFrame frame; + guint64 start; + guint64 end; + guint64 offset; + guint64 inode; + gchar filename[0]; +} SpCaptureMap; + +typedef struct +{ + SpCaptureFrame frame; + guint32 n_jitmaps; + guint8 data[0]; +} SpCaptureJitmap; + +typedef struct +{ + SpCaptureFrame frame; + gchar cmdline[0]; +} SpCaptureProcess; + +typedef struct +{ + SpCaptureFrame frame; + guint16 n_addrs; + guint64 padding : 48; + SpCaptureAddress addrs[0]; +} SpCaptureSample; + +typedef struct +{ + SpCaptureFrame frame; + GPid child_pid; +} SpCaptureFork; + +typedef struct +{ + SpCaptureFrame frame; +} SpCaptureExit; + +typedef struct +{ + SpCaptureFrame frame; +} SpCaptureTimestamp; + +#pragma pack(pop) + +G_STATIC_ASSERT (sizeof (SpCaptureFileHeader) == 256); +G_STATIC_ASSERT (sizeof (SpCaptureFrame) == 24); +G_STATIC_ASSERT (sizeof (SpCaptureMap) == 56); +G_STATIC_ASSERT (sizeof (SpCaptureJitmap) == 28); +G_STATIC_ASSERT (sizeof (SpCaptureProcess) == 24); +G_STATIC_ASSERT (sizeof (SpCaptureSample) == 32); +G_STATIC_ASSERT (sizeof (SpCaptureFork) == 28); +G_STATIC_ASSERT (sizeof (SpCaptureExit) == 24); +G_STATIC_ASSERT (sizeof (SpCaptureTimestamp) == 24); + +static inline gint +sp_capture_address_compare (SpCaptureAddress a, + SpCaptureAddress b) +{ + if (a < b) + return -1; + if (a > b) + return 1; + else + return 0; +} + +G_END_DECLS + +#endif /* SP_CAPTURE_FORMAT_H */ diff --git a/lib/sp-capture-writer.c b/lib/sp-capture-writer.c new file mode 100644 index 00000000..f0f725f2 --- /dev/null +++ b/lib/sp-capture-writer.c @@ -0,0 +1,954 @@ +/* sp-capture-writer.c + * + * Copyright (C) 2016 Christian Hergert + * + * This file 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 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License + * along with this program. If not, see . + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sp-capture-reader.h" +#include "sp-capture-writer.h" + +#define DEFAULT_BUFFER_SIZE (getpagesize() * 64L) +#define INVALID_ADDRESS (G_GUINT64_CONSTANT(0)) + +typedef struct +{ + /* A pinter into the string buffer */ + const gchar *str; + + /* The unique address for the string */ + guint64 addr; +} SpCaptureJitmapBucket; + +struct _SpCaptureWriter +{ + /* + * This is our buffer location for incoming strings. This is used + * similarly to GStringChunk except there is only one-page, and after + * it fills, we flush to disk. + * + * This is paired with a closed hash table for deduplication. + */ + gchar addr_buf[4096*4]; + + /* Our hashtable for deduplication. */ + SpCaptureJitmapBucket addr_hash[512]; + + /* We keep the large fields above so that our allocation will be page + * alinged for the write buffer. This improves the performance of large + * writes to the target file-descriptor. + */ + + volatile gint ref_count; + + /* + * Our address sequence counter. The value that comes from + * monotonically increasing this is OR'd with JITMAP_MARK to denote + * the address name should come from the JIT map. + */ + gsize addr_seq; + + /* Our position in addr_buf. */ + gsize addr_buf_pos; + + /* + * The number of hash table items in @addr_hash. This is an + * optimization so that we can avoid calculating the number of strings + * when flushing out the jitmap. + */ + guint addr_hash_size; + + /* Capture file handle */ + int fd; + + /* Our write buffer for fd */ + guint8 *buf; + gsize pos; + gsize len; + + /* Statistics while recording */ + SpCaptureStat stat; +}; + +#ifndef SP_DISABLE_GOBJECT +G_DEFINE_BOXED_TYPE (SpCaptureWriter, sp_capture_writer, + sp_capture_writer_ref, sp_capture_writer_unref) +#endif + +static void +sp_capture_writer_finalize (SpCaptureWriter *self) +{ + if (self != NULL) + { + sp_capture_writer_flush (self); + close (self->fd); + g_free (self->buf); + g_free (self); + } +} + +SpCaptureWriter * +sp_capture_writer_ref (SpCaptureWriter *self) +{ + g_assert (self != NULL); + g_assert (self->ref_count > 0); + + g_atomic_int_inc (&self->ref_count); + + return self; +} + +void +sp_capture_writer_unref (SpCaptureWriter *self) +{ + g_assert (self != NULL); + g_assert (self->ref_count > 0); + + if (g_atomic_int_dec_and_test (&self->ref_count)) + sp_capture_writer_finalize (self); +} + +static gboolean +sp_capture_writer_flush_data (SpCaptureWriter *self) +{ + const guint8 *buf; + gssize written; + gsize to_write; + + g_assert (self != NULL); + g_assert (self->pos <= self->len); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + buf = self->buf; + to_write = self->pos; + + while (to_write > 0) + { + written = write (self->fd, buf, to_write); + if (written < 0) + return FALSE; + + if (written == 0 && errno != EAGAIN) + return FALSE; + + g_assert (written <= (gssize)to_write); + + buf += written; + to_write -= written; + } + + self->pos = 0; + + return TRUE; +} + +static inline void +sp_capture_writer_realign (gsize *pos) +{ + *pos = (*pos + SP_CAPTURE_ALIGN - 1) & ~(SP_CAPTURE_ALIGN - 1); +} + +static inline gboolean +sp_capture_writer_ensure_space_for (SpCaptureWriter *self, + gsize len) +{ + if ((self->len - self->pos) < len) + { + if (!sp_capture_writer_flush_data (self)) + return FALSE; + } + + return TRUE; +} + +static gboolean +sp_capture_writer_flush_jitmap (SpCaptureWriter *self) +{ + SpCaptureJitmap jitmap; + gssize r; + gsize len; + + g_assert (self != NULL); + + if (self->addr_hash_size == 0) + return TRUE; + + g_assert (self->addr_buf_pos > 0); + + len = sizeof jitmap + self->addr_buf_pos; + + sp_capture_writer_realign (&len); + + jitmap.frame.len = len; + jitmap.frame.cpu = -1; + jitmap.frame.pid = getpid (); + jitmap.frame.time = g_get_monotonic_time (); + jitmap.frame.type = SP_CAPTURE_FRAME_JITMAP; + jitmap.n_jitmaps = self->addr_hash_size; + + if (sizeof jitmap != write (self->fd, &jitmap, sizeof jitmap)) + return FALSE; + + r = write (self->fd, self->addr_buf, len - sizeof jitmap); + if (r < 0 || (gsize)r != (len - sizeof jitmap)) + return FALSE; + + self->addr_buf_pos = 0; + self->addr_hash_size = 0; + memset (self->addr_hash, 0, sizeof self->addr_hash); + + self->stat.frame_count[SP_CAPTURE_FRAME_JITMAP]++; + + return TRUE; +} + +static gboolean +sp_capture_writer_lookup_jitmap (SpCaptureWriter *self, + const gchar *name, + SpCaptureAddress *addr) +{ + guint hash; + guint i; + + g_assert (self != NULL); + g_assert (name != NULL); + g_assert (addr != NULL); + + hash = g_str_hash (name) % G_N_ELEMENTS (self->addr_hash); + + for (i = hash; i < G_N_ELEMENTS (self->addr_hash); i++) + { + SpCaptureJitmapBucket *bucket = &self->addr_hash[i]; + + if (bucket->str == NULL) + return FALSE; + + if (strcmp (bucket->str, name) == 0) + { + *addr = bucket->addr; + return TRUE; + } + } + + for (i = 0; i < hash; i++) + { + SpCaptureJitmapBucket *bucket = &self->addr_hash[i]; + + if (bucket->str == NULL) + return FALSE; + + if (strcmp (bucket->str, name) == 0) + { + *addr = bucket->addr; + return TRUE; + } + } + + return FALSE; +} + +static SpCaptureAddress +sp_capture_writer_insert_jitmap (SpCaptureWriter *self, + const gchar *str) +{ + SpCaptureAddress addr; + gchar *dst; + gsize len; + guint hash; + guint i; + + g_assert (self != NULL); + g_assert (str != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + len = sizeof addr + strlen (str) + 1; + + if ((self->addr_hash_size == G_N_ELEMENTS (self->addr_hash)) || + ((sizeof self->addr_buf - self->addr_buf_pos) < len)) + { + if (!sp_capture_writer_flush_jitmap (self)) + return INVALID_ADDRESS; + + g_assert (self->addr_hash_size == 0); + g_assert (self->addr_buf_pos == 0); + } + + g_assert (self->addr_hash_size < G_N_ELEMENTS (self->addr_hash)); + g_assert (len > sizeof addr); + + /* Allocate the next unique address */ + addr = SP_CAPTURE_JITMAP_MARK | ++self->addr_seq; + + /* Copy the address into the buffer */ + dst = (gchar *)&self->addr_buf[self->addr_buf_pos]; + memcpy (dst, &addr, sizeof addr); + + /* + * Copy the string into the buffer, keeping dst around for + * when we insert into the hashtable. + */ + dst += sizeof addr; + memcpy (dst, str, len - sizeof addr); + + /* Advance our string cache position */ + self->addr_buf_pos += len; + g_assert (self->addr_buf_pos <= sizeof self->addr_buf); + + /* Now place the address into the hashtable */ + hash = g_str_hash (str) % G_N_ELEMENTS (self->addr_hash); + + /* Start from the current hash bucket and go forward */ + for (i = hash; i < G_N_ELEMENTS (self->addr_hash); i++) + { + SpCaptureJitmapBucket *bucket = &self->addr_hash[i]; + + if (G_LIKELY (bucket->str == NULL)) + { + bucket->str = dst; + bucket->addr = addr; + self->addr_hash_size++; + return addr; + } + } + + /* Wrap around to the beginning */ + for (i = 0; i < hash; i++) + { + SpCaptureJitmapBucket *bucket = &self->addr_hash[i]; + + if (G_LIKELY (bucket->str == NULL)) + { + bucket->str = dst; + bucket->addr = addr; + self->addr_hash_size++; + return addr; + } + } + + g_assert_not_reached (); + + return INVALID_ADDRESS; +} + +SpCaptureWriter * +sp_capture_writer_new_from_fd (int fd, + gsize buffer_size) +{ + g_autofree gchar *nowstr = NULL; + SpCaptureWriter *self; + SpCaptureFileHeader *header; + GTimeVal tv; + + if (buffer_size == 0) + buffer_size = DEFAULT_BUFFER_SIZE; + + g_assert (fd != -1); + g_assert (buffer_size % getpagesize() == 0); + + self = g_new0 (SpCaptureWriter, 1); + self->ref_count = 1; + self->fd = fd; + self->buf = (guint8 *)g_malloc (buffer_size); + self->len = buffer_size; + + g_get_current_time (&tv); + nowstr = g_time_val_to_iso8601 (&tv); + + header = (SpCaptureFileHeader *)(gpointer)self->buf; + header->magic = SP_CAPTURE_MAGIC; + header->version = 1; +#ifdef G_LITTLE_ENDIAN + header->little_endian = TRUE; +#else + header->little_endian = FALSE; +#endif + header->padding = 0; + g_strlcpy (header->capture_time, nowstr, sizeof header->capture_time); + memset (header->suffix, 0, sizeof header->suffix); + + self->pos += sizeof *header; + + if (!sp_capture_writer_flush_data (self)) + { + sp_capture_writer_finalize (self); + return NULL; + } + + g_assert (self->pos == 0); + g_assert (self->len > 0); + g_assert (self->len % getpagesize() == 0); + g_assert (self->buf != NULL); + g_assert (self->addr_hash_size == 0); + g_assert (self->fd != -1); + + return self; +} + +SpCaptureWriter * +sp_capture_writer_new (const gchar *filename, + gsize buffer_size) +{ + SpCaptureWriter *self; + int fd; + + g_assert (filename != NULL); + g_assert (buffer_size % getpagesize() == 0); + + if ((-1 == (fd = open (filename, O_CREAT | O_RDWR, 0640))) || + (-1 == ftruncate (fd, 0L))) + return NULL; + + self = sp_capture_writer_new_from_fd (fd, buffer_size); + + if (self == NULL) + close (fd); + + return self; +} + +gboolean +sp_capture_writer_add_map (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + guint64 start, + guint64 end, + guint64 offset, + guint64 inode, + const gchar *filename) +{ + SpCaptureMap *ev; + gsize len; + + if (filename == NULL) + filename = ""; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (filename != NULL); + + len = sizeof *ev + strlen (filename) + 1; + + sp_capture_writer_realign (&len); + + if (!sp_capture_writer_ensure_space_for (self, len)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + ev = (SpCaptureMap *)(gpointer)&self->buf[self->pos]; + ev->frame.len = len; + ev->frame.cpu = cpu; + ev->frame.pid = pid; + ev->frame.time = time; + ev->frame.type = SP_CAPTURE_FRAME_MAP; + ev->frame.padding = 0; + ev->start = start; + ev->end = end; + ev->offset = offset; + ev->inode = inode; + + g_strlcpy (ev->filename, filename, len - sizeof *ev); + ev->filename[len - sizeof *ev - 1] = '\0'; + + self->pos += ev->frame.len; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + self->stat.frame_count[SP_CAPTURE_FRAME_MAP]++; + + return TRUE; +} + +SpCaptureAddress +sp_capture_writer_add_jitmap (SpCaptureWriter *self, + const gchar *name) +{ + SpCaptureAddress addr = INVALID_ADDRESS; + + if (name == NULL) + name = ""; + + g_assert (self != NULL); + g_assert (name != NULL); + + if (!sp_capture_writer_lookup_jitmap (self, name, &addr)) + addr = sp_capture_writer_insert_jitmap (self, name); + + return addr; +} + +gboolean +sp_capture_writer_add_process (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + const gchar *cmdline) +{ + SpCaptureProcess *ev; + gsize len; + + if (cmdline == NULL) + cmdline = ""; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (cmdline != NULL); + + len = sizeof *ev + strlen (cmdline) + 1; + + sp_capture_writer_realign (&len); + + if (!sp_capture_writer_ensure_space_for (self, len)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + ev = (SpCaptureProcess *)(gpointer)&self->buf[self->pos]; + ev->frame.len = len; + ev->frame.cpu = cpu; + ev->frame.pid = pid; + ev->frame.time = time; + ev->frame.type = SP_CAPTURE_FRAME_PROCESS; + ev->frame.padding = 0; + + g_strlcpy (ev->cmdline, cmdline, len - sizeof *ev); + ev->cmdline[len - sizeof *ev - 1] = '\0'; + + self->pos += ev->frame.len; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + self->stat.frame_count[SP_CAPTURE_FRAME_PROCESS]++; + + return TRUE; +} + +gboolean +sp_capture_writer_add_sample (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + const SpCaptureAddress *addrs, + guint n_addrs) +{ + SpCaptureSample *ev; + gsize len; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + len = sizeof *ev + (n_addrs * sizeof (SpCaptureAddress)); + + sp_capture_writer_realign (&len); + + if (!sp_capture_writer_ensure_space_for (self, len)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + ev = (SpCaptureSample *)(gpointer)&self->buf[self->pos]; + ev->frame.len = len; + ev->frame.cpu = cpu; + ev->frame.pid = pid; + ev->frame.time = time; + ev->frame.type = SP_CAPTURE_FRAME_SAMPLE; + ev->frame.padding = 0; + ev->n_addrs = n_addrs; + + memcpy (ev->addrs, addrs, (n_addrs * sizeof (SpCaptureAddress))); + + self->pos += ev->frame.len; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + self->stat.frame_count[SP_CAPTURE_FRAME_SAMPLE]++; + + return TRUE; +} + +gboolean +sp_capture_writer_add_fork (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + GPid child_pid) +{ + SpCaptureFork *ev; + gsize len = sizeof *ev; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + sp_capture_writer_realign (&len); + + if (!sp_capture_writer_ensure_space_for (self, len)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + ev = (SpCaptureFork *)(gpointer)&self->buf[self->pos]; + ev->frame.len = len; + ev->frame.cpu = cpu; + ev->frame.pid = pid; + ev->frame.time = time; + ev->frame.type = SP_CAPTURE_FRAME_FORK; + ev->frame.padding = 0; + ev->child_pid = child_pid; + + self->pos += ev->frame.len; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + self->stat.frame_count[SP_CAPTURE_FRAME_FORK]++; + + return TRUE; +} + +gboolean +sp_capture_writer_add_exit (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid) +{ + SpCaptureExit *ev; + gsize len = sizeof *ev; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + sp_capture_writer_realign (&len); + + if (!sp_capture_writer_ensure_space_for (self, len)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + ev = (SpCaptureExit *)(gpointer)&self->buf[self->pos]; + ev->frame.len = len; + ev->frame.cpu = cpu; + ev->frame.pid = pid; + ev->frame.time = time; + ev->frame.type = SP_CAPTURE_FRAME_EXIT; + ev->frame.padding = 0; + + self->pos += ev->frame.len; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + self->stat.frame_count[SP_CAPTURE_FRAME_EXIT]++; + + return TRUE; +} + +gboolean +sp_capture_writer_add_timestamp (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid) +{ + SpCaptureTimestamp *ev; + gsize len = sizeof *ev; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + sp_capture_writer_realign (&len); + + if (!sp_capture_writer_ensure_space_for (self, len)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + ev = (SpCaptureTimestamp *)(gpointer)&self->buf[self->pos]; + ev->frame.len = len; + ev->frame.cpu = cpu; + ev->frame.pid = pid; + ev->frame.time = time; + ev->frame.type = SP_CAPTURE_FRAME_TIMESTAMP; + ev->frame.padding = 0; + + self->pos += ev->frame.len; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + self->stat.frame_count[SP_CAPTURE_FRAME_TIMESTAMP]++; + + return TRUE; +} + +gboolean +sp_capture_writer_flush (SpCaptureWriter *self) +{ + g_assert (self != NULL); + + return (sp_capture_writer_flush_jitmap (self) && + sp_capture_writer_flush_data (self)); +} + +/** + * sp_capture_writer_save_as: + * @self: A #SpCaptureWriter + * @filename: the file to save the capture as + * @error: a location for a #GError or %NULL. + * + * Saves the captured data as the file @filename. + * + * This is primarily useful if the writer was created with a memory-backed + * file-descriptor such as a memfd or tmpfs file on Linux. + * + * Returns: %TRUE if successful, otherwise %FALSE and @error is set. + */ +gboolean +sp_capture_writer_save_as (SpCaptureWriter *self, + const gchar *filename, + GError **error) +{ + gsize to_write; + off_t in_off; + off_t pos; + int fd = -1; + + g_assert (self != NULL); + g_assert (self->fd != -1); + g_assert (filename != NULL); + + if (-1 == (fd = open (filename, O_CREAT | O_RDWR, 0640))) + goto handle_errno; + + if (!sp_capture_writer_flush (self)) + goto handle_errno; + + if (-1 == (pos = lseek (self->fd, 0L, SEEK_CUR))) + goto handle_errno; + + to_write = pos; + in_off = 0; + + while (to_write > 0) + { + gssize written; + + written = sendfile (fd, self->fd, &in_off, pos); + + if (written < 0) + goto handle_errno; + + if (written == 0 && errno != EAGAIN) + goto handle_errno; + + g_assert (written <= (gssize)to_write); + + to_write -= written; + } + + close (fd); + + return TRUE; + +handle_errno: + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + + if (fd != -1) + { + close (fd); + g_unlink (filename); + } + + return FALSE; +} + +/** + * _sp_capture_writer_splice_from_fd: + * @self: An #SpCaptureWriter + * @fd: the fd to read from. + * @error: A location for a #GError, or %NULL. + * + * This is internal API for SpCaptureWriter and SpCaptureReader to + * communicate when splicing a reader into a writer. + * + * This should not be used outside of #SpCaptureReader or + * #SpCaptureWriter. + * + * This will not advance the position of @fd. + * + * Returns: %TRUE if successful; otherwise %FALSE and @error is set. + */ +gboolean +_sp_capture_writer_splice_from_fd (SpCaptureWriter *self, + int fd, + GError **error) +{ + struct stat stbuf; + off_t in_off; + gsize to_write; + + g_assert (self != NULL); + g_assert (self->fd != -1); + + if (-1 == fstat (fd, &stbuf)) + goto handle_errno; + + if (stbuf.st_size < 256) + { + g_set_error (error, + G_FILE_ERROR, + G_FILE_ERROR_INVAL, + "Cannot splice, possibly corrupt file."); + return FALSE; + } + + in_off = 256; + to_write = stbuf.st_size - in_off; + + while (to_write > 0) + { + gssize written; + + written = sendfile (self->fd, fd, &in_off, to_write); + + if (written < 0) + goto handle_errno; + + if (written == 0 && errno != EAGAIN) + goto handle_errno; + + g_assert (written <= (gssize)to_write); + + to_write -= written; + } + + return TRUE; + +handle_errno: + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + + return FALSE; +} + +/** + * sp_capture_writer_splice: + * @self: An #SpCaptureWriter + * @dest: An #SpCaptureWriter + * @error: A location for a #GError, or %NULL. + * + * This function will copy the capture @self into the capture @dest. This + * tries to be semi-efficient by using sendfile() to copy the contents between + * the captures. @self and @dest will be flushed before the contents are copied + * into the @dest file-descriptor. + * + * Returns: %TRUE if successful, otherwise %FALSE and and @error is set. + */ +gboolean +sp_capture_writer_splice (SpCaptureWriter *self, + SpCaptureWriter *dest, + GError **error) +{ + gboolean ret; + off_t pos; + + g_assert (self != NULL); + g_assert (self->fd != -1); + g_assert (dest != NULL); + g_assert (dest->fd != -1); + + /* Flush before writing anything to ensure consistency */ + if (!sp_capture_writer_flush (self) || !sp_capture_writer_flush (dest)) + goto handle_errno; + + /* Track our current position so we can reset */ + if ((off_t)-1 == (pos = lseek (self->fd, 0L, SEEK_CUR))) + goto handle_errno; + + /* Perform the splice */ + ret = _sp_capture_writer_splice_from_fd (dest, self->fd, error); + + /* Now reset or file-descriptor position (it should be the same */ + if (pos != lseek (self->fd, pos, SEEK_SET)) + { + ret = FALSE; + goto handle_errno; + } + + return ret; + +handle_errno: + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + + return FALSE; +} + +/** + * sp_capture_writer_create_reader: + * @self: A #SpCaptureWriter + * @error: a location for a #GError, or %NULL + * + * Creates a new reader for the writer. + * + * Since readers use positioned reads, this uses the same file-descriptor for + * the #SpCaptureReader. Therefore, if you are writing to the capture while + * also consuming from the reader, you could get transient failures unless you + * synchronize the operations. + * + * Returns: (transfer full): A #SpCaptureReader. + */ +SpCaptureReader * +sp_capture_writer_create_reader (SpCaptureWriter *self, + GError **error) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (self->fd != -1, NULL); + + return sp_capture_reader_new_from_fd (self->fd, error); +} + +/** + * sp_capture_writer_stat: + * @self: A #SpCaptureWriter + * @stat: (out): A location for an #SpCaptureStat + * + * This function will fill @stat with statistics generated while capturing + * the profiler session. + */ +void +sp_capture_writer_stat (SpCaptureWriter *self, + SpCaptureStat *stat) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (stat != NULL); + + *stat = self->stat; +} diff --git a/lib/sp-capture-writer.h b/lib/sp-capture-writer.h new file mode 100644 index 00000000..1846fd1f --- /dev/null +++ b/lib/sp-capture-writer.h @@ -0,0 +1,108 @@ +/* sp-capture-writer.h + * + * Copyright (C) 2016 Christian Hergert + * + * This file 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 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License + * along with this program. If not, see . + */ + +#ifndef SP_CAPTURE_WRITER_H +#define SP_CAPTURE_WRITER_H + +#include "sp-capture-types.h" + +G_BEGIN_DECLS + +typedef struct _SpCaptureWriter SpCaptureWriter; + +typedef struct +{ + /* + * The number of frames indexed by SpCaptureFrameType + */ + gsize frame_count[16]; + + /* + * Padding for future expansion. + */ + gsize padding[48]; +} SpCaptureStat; + +SpCaptureWriter *sp_capture_writer_new (const gchar *filename, + gsize buffer_size); +SpCaptureWriter *sp_capture_writer_new_from_fd (int fd, + gsize buffer_size); +SpCaptureWriter *sp_capture_writer_ref (SpCaptureWriter *self); +void sp_capture_writer_unref (SpCaptureWriter *self); +void sp_capture_writer_stat (SpCaptureWriter *self, + SpCaptureStat *stat); +gboolean sp_capture_writer_add_map (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + guint64 start, + guint64 end, + guint64 offset, + guint64 inode, + const gchar *filename); +guint64 sp_capture_writer_add_jitmap (SpCaptureWriter *self, + const gchar *name); +gboolean sp_capture_writer_add_process (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + const gchar *cmdline); +gboolean sp_capture_writer_add_sample (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + const SpCaptureAddress *addrs, + guint n_addrs); +gboolean sp_capture_writer_add_fork (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + GPid child_pid); +gboolean sp_capture_writer_add_exit (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid); +gboolean sp_capture_writer_add_timestamp (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid); +gboolean sp_capture_writer_flush (SpCaptureWriter *self); +gboolean sp_capture_writer_save_as (SpCaptureWriter *self, + const gchar *filename, + GError **error); +SpCaptureReader *sp_capture_writer_create_reader (SpCaptureWriter *self, + GError **error); +gboolean sp_capture_writer_splice (SpCaptureWriter *self, + SpCaptureWriter *dest, + GError **error); +gboolean _sp_capture_writer_splice_from_fd (SpCaptureWriter *self, + int fd, + GError **error) G_GNUC_INTERNAL; + +#ifndef SP_DISABLE_GOBJECT +# define SP_TYPE_CAPTURE_WRITER (sp_capture_writer_get_type()) + GType sp_capture_writer_get_type (void); +#endif + +#if GLIB_CHECK_VERSION(2, 44, 0) + G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpCaptureWriter, sp_capture_writer_unref) +#endif + +G_END_DECLS + +#endif /* SP_CAPTURE_WRITER_H */ diff --git a/lib/sp-cell-renderer-percent.c b/lib/sp-cell-renderer-percent.c new file mode 100644 index 00000000..4f5fe2b1 --- /dev/null +++ b/lib/sp-cell-renderer-percent.c @@ -0,0 +1,135 @@ +/* sp-cell-renderer-percent.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-cell-renderer-percent.h" + +typedef struct +{ + gdouble percent; +} SpCellRendererPercentPrivate; + +enum { + PROP_0, + PROP_PERCENT, + N_PROPS +}; + +G_DEFINE_TYPE_WITH_PRIVATE (SpCellRendererPercent, sp_cell_renderer_percent, GTK_TYPE_CELL_RENDERER_TEXT) + +static GParamSpec *properties [N_PROPS]; + +static void +sp_cell_renderer_percent_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpCellRendererPercent *self = SP_CELL_RENDERER_PERCENT (object); + + switch (prop_id) + { + case PROP_PERCENT: + g_value_set_double (value, sp_cell_renderer_percent_get_percent (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_cell_renderer_percent_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpCellRendererPercent *self = SP_CELL_RENDERER_PERCENT (object); + + switch (prop_id) + { + case PROP_PERCENT: + sp_cell_renderer_percent_set_percent (self, g_value_get_double (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_cell_renderer_percent_class_init (SpCellRendererPercentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sp_cell_renderer_percent_get_property; + object_class->set_property = sp_cell_renderer_percent_set_property; + + properties [PROP_PERCENT] = + g_param_spec_double ("percent", + "Percent", + "Percent", + 0.0, + 100.0, + 0.0, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sp_cell_renderer_percent_init (SpCellRendererPercent *self) +{ + g_object_set (self, "xalign", 1.0f, NULL); +} + +gdouble +sp_cell_renderer_percent_get_percent (SpCellRendererPercent *self) +{ + SpCellRendererPercentPrivate *priv = sp_cell_renderer_percent_get_instance_private (self); + + g_return_val_if_fail (SP_IS_CELL_RENDERER_PERCENT (self), 0.0); + + return priv->percent; +} + +void +sp_cell_renderer_percent_set_percent (SpCellRendererPercent *self, + gdouble percent) +{ + SpCellRendererPercentPrivate *priv = sp_cell_renderer_percent_get_instance_private (self); + + g_return_if_fail (SP_IS_CELL_RENDERER_PERCENT (self)); + g_return_if_fail (percent >= 0.0); + g_return_if_fail (percent <= 100.0); + + if (percent != priv->percent) + { + gchar text[128]; + + priv->percent = percent; + + g_snprintf (text, sizeof text, "%.2lf %%", percent); + text [sizeof text - 1] = '\0'; + + g_object_set (self, "markup", text, NULL); + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PERCENT]); + } +} diff --git a/lib/sp-cell-renderer-percent.h b/lib/sp-cell-renderer-percent.h new file mode 100644 index 00000000..1c747b26 --- /dev/null +++ b/lib/sp-cell-renderer-percent.h @@ -0,0 +1,55 @@ +/* sp-cell-renderer-percent.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_CELL_RENDERER_PERCENT_H +#define SP_CELL_RENDERER_PERCENT_H + +#include + +G_BEGIN_DECLS + +#define SP_TYPE_CELL_RENDERER_PERCENT (sp_cell_renderer_percent_get_type()) +#define SP_CELL_RENDERER_PERCENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CELL_RENDERER_PERCENT, SpCellRendererPercent)) +#define SP_CELL_RENDERER_PERCENT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CELL_RENDERER_PERCENT, SpCellRendererPercent const)) +#define SP_CELL_RENDERER_PERCENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_CELL_RENDERER_PERCENT, SpCellRendererPercentClass)) +#define SP_IS_CELL_RENDERER_PERCENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CELL_RENDERER_PERCENT)) +#define SP_IS_CELL_RENDERER_PERCENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_CELL_RENDERER_PERCENT)) +#define SP_CELL_RENDERER_PERCENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SP_TYPE_CELL_RENDERER_PERCENT, SpCellRendererPercentClass)) + +typedef struct _SpCellRendererPercent SpCellRendererPercent; +typedef struct _SpCellRendererPercentClass SpCellRendererPercentClass; + +struct _SpCellRendererPercent +{ + GtkCellRendererText parent; +}; + +struct _SpCellRendererPercentClass +{ + GtkCellRendererTextClass parent_class; +}; + +GType sp_cell_renderer_percent_get_type (void); +GtkCellRenderer *sp_cell_renderer_percent_new (void); +gdouble sp_cell_renderer_percent_get_percent (SpCellRendererPercent *self); +void sp_cell_renderer_percent_set_percent (SpCellRendererPercent *self, + gdouble percent); + +G_END_DECLS + +#endif /* SP_CELL_RENDERER_PERCENT_H */ diff --git a/lib/sp-clock.c b/lib/sp-clock.c new file mode 100644 index 00000000..4d34ad82 --- /dev/null +++ b/lib/sp-clock.c @@ -0,0 +1,51 @@ +/* sp-clock.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-clock.h" + +gint sp_clock = -1; + +void +sp_clock_init (void) +{ + static const gint clock_ids[] = { + CLOCK_MONOTONIC_RAW, + CLOCK_MONOTONIC_COARSE, + CLOCK_MONOTONIC, + CLOCK_REALTIME_COARSE, + CLOCK_REALTIME, + }; + guint i; + + if (sp_clock != -1) + return; + + for (i = 0; i < G_N_ELEMENTS (clock_ids); i++) + { + struct timespec ts; + int clock_id = clock_ids [i]; + + if (0 == clock_gettime (clock_id, &ts)) + { + sp_clock = clock_id; + return; + } + } + + g_assert_not_reached (); +} diff --git a/lib/sp-clock.h b/lib/sp-clock.h new file mode 100644 index 00000000..a38c1721 --- /dev/null +++ b/lib/sp-clock.h @@ -0,0 +1,54 @@ +/* sp-clock.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_CLOCK_H +#define SP_CLOCK_H + +#include +#include + +G_BEGIN_DECLS + +typedef gint SpClock; +typedef gint64 SpTimeStamp; +typedef gint32 SpTimeSpan; + +extern SpClock sp_clock; + +static inline SpTimeStamp +sp_clock_get_current_time (void) +{ + struct timespec ts; + + clock_gettime (sp_clock, &ts); + + return (ts.tv_sec * G_GINT64_CONSTANT (1000000000)) + ts.tv_nsec; +} + +static inline SpTimeSpan +sp_clock_get_relative_time (SpTimeStamp epoch) +{ + return sp_clock_get_current_time () - epoch; +} + +void sp_clock_init (void); + +G_END_DECLS + +#endif /* SP_CLOCK_H */ + diff --git a/lib/sp-elf-symbol-resolver.c b/lib/sp-elf-symbol-resolver.c new file mode 100644 index 00000000..2c7cf239 --- /dev/null +++ b/lib/sp-elf-symbol-resolver.c @@ -0,0 +1,276 @@ +/* sp-elf-symbol-resolver.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "binfile.h" +#include "elfparser.h" + +#include "sp-map-lookaside.h" +#include "sp-elf-symbol-resolver.h" + +struct _SpElfSymbolResolver +{ + GObject parent_instance; + + GHashTable *lookasides; + GHashTable *bin_files; + GHashTable *tag_cache; +}; + +static void symbol_resolver_iface_init (SpSymbolResolverInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpElfSymbolResolver, + sp_elf_symbol_resolver, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE (SP_TYPE_SYMBOL_RESOLVER, + symbol_resolver_iface_init)) + +static void +sp_elf_symbol_resolver_finalize (GObject *object) +{ + SpElfSymbolResolver *self = (SpElfSymbolResolver *)object; + + g_clear_pointer (&self->bin_files, g_hash_table_unref); + g_clear_pointer (&self->lookasides, g_hash_table_unref); + g_clear_pointer (&self->tag_cache, g_hash_table_unref); + + G_OBJECT_CLASS (sp_elf_symbol_resolver_parent_class)->finalize (object); +} + +static void +sp_elf_symbol_resolver_class_init (SpElfSymbolResolverClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_elf_symbol_resolver_finalize; +} + +static void +sp_elf_symbol_resolver_init (SpElfSymbolResolver *self) +{ + self->lookasides = g_hash_table_new_full (NULL, + NULL, + NULL, + (GDestroyNotify)sp_map_lookaside_free); + + self->bin_files = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)bin_file_free); + + self->tag_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); +} + +static void +sp_elf_symbol_resolver_load (SpSymbolResolver *resolver, + SpCaptureReader *reader) +{ + SpElfSymbolResolver *self = (SpElfSymbolResolver *)resolver; + SpCaptureFrameType type; + + g_assert (SP_IS_SYMBOL_RESOLVER (resolver)); + g_assert (reader != NULL); + + sp_capture_reader_reset (reader); + + while (sp_capture_reader_peek_type (reader, &type)) + { + const SpCaptureMap *ev; + SpMapLookaside *lookaside; + SpMap map; + + if (type != SP_CAPTURE_FRAME_MAP) + { + if (!sp_capture_reader_skip (reader)) + return; + continue; + } + + ev = sp_capture_reader_read_map (reader); + + map.start = ev->start; + map.end = ev->end; + map.offset = ev->offset; + map.inode = ev->inode; + map.filename = ev->filename; + + lookaside = g_hash_table_lookup (self->lookasides, GINT_TO_POINTER (ev->frame.pid)); + + if (lookaside == NULL) + { + lookaside = sp_map_lookaside_new (); + g_hash_table_insert (self->lookasides, GINT_TO_POINTER (ev->frame.pid), lookaside); + } + + sp_map_lookaside_insert (lookaside, &map); + } +} + +static bin_file_t * +sp_elf_symbol_resolver_get_bin_file (SpElfSymbolResolver *self, + const gchar *filename) +{ + bin_file_t *bin_file; + + g_assert (SP_IS_ELF_SYMBOL_RESOLVER (self)); + + bin_file = g_hash_table_lookup (self->bin_files, filename); + + if (bin_file == NULL) + { + bin_file = bin_file_new (filename); + g_hash_table_insert (self->bin_files, g_strdup (filename), bin_file); + } + + return bin_file; +} + +static GQuark +guess_tag (SpElfSymbolResolver *self, + const SpMap *map) +{ + g_assert (map != NULL); + g_assert (map->filename != NULL); + + if (!g_hash_table_contains (self->tag_cache, map->filename)) + { + GQuark tag = 0; + + if (strstr (map->filename, "/libgobject-2.0.")) + tag = g_quark_from_static_string ("GObject"); + + else if (strstr (map->filename, "/libglib-2.0.")) + tag = g_quark_from_static_string ("GLib"); + + else if (strstr (map->filename, "/libgio-2.0.")) + tag = g_quark_from_static_string ("Gio"); + + else if (strstr (map->filename, "/libgtk-3.")) + tag = g_quark_from_static_string ("Gtk+"); + + else if (strstr (map->filename, "/libgdk-3.")) + tag = g_quark_from_static_string ("Gdk"); + + else if (strstr (map->filename, "/libpixman-1")) + tag = g_quark_from_static_string ("Pixman"); + + else if (strstr (map->filename, "/libcairo.")) + tag = g_quark_from_static_string ("cairo"); + + else if (strstr (map->filename, "/libgstreamer-1.")) + tag = g_quark_from_static_string ("GStreamer"); + + else if (strstr (map->filename, "/libX11.")) + tag = g_quark_from_static_string ("X11"); + + else if (strstr (map->filename, "/libpango-1.0.")) + tag = g_quark_from_static_string ("Pango"); + + else if (strstr (map->filename, "/libclutter-")) + tag = g_quark_from_static_string ("Clutter"); + + else if (strstr (map->filename, "/libcogl.") || + strstr (map->filename, "/libcogl-")) + tag = g_quark_from_static_string ("Cogl"); + + else if (strstr (map->filename, "/libffi.")) + tag = g_quark_from_static_string ("libffi"); + + else if (strstr (map->filename, "/libwayland-")) + tag = g_quark_from_static_string ("Wayland"); + + else if (strstr (map->filename, "/libinput.")) + tag = g_quark_from_static_string ("libinput"); + + else if (strstr (map->filename, "/libgjs.")) + tag = g_quark_from_static_string ("Gjs"); + + else if (strstr (map->filename, "/libmozjs-")) + tag = g_quark_from_static_string ("MozJS"); + + else if (strstr (map->filename, "/libGL.")) + tag = g_quark_from_static_string ("GL"); + + else if (strstr (map->filename, "/libEGL.")) + tag = g_quark_from_static_string ("EGL"); + + g_hash_table_insert (self->tag_cache, + g_strdup (map->filename), + GSIZE_TO_POINTER (tag)); + } + + return GPOINTER_TO_SIZE (g_hash_table_lookup (self->tag_cache, map->filename)); +} + +static gchar * +sp_elf_symbol_resolver_resolve (SpSymbolResolver *resolver, + guint64 time, + GPid pid, + SpCaptureAddress address, + GQuark *tag) +{ + SpElfSymbolResolver *self = (SpElfSymbolResolver *)resolver; + const bin_symbol_t *bin_sym; + SpMapLookaside *lookaside; + const gchar *bin_sym_name; + const SpMap *map; + bin_file_t *bin_file; + + g_assert (SP_IS_ELF_SYMBOL_RESOLVER (self)); + + lookaside = g_hash_table_lookup (self->lookasides, GINT_TO_POINTER (pid)); + if (lookaside == NULL) + return NULL; + + map = sp_map_lookaside_lookup (lookaside, address); + if (map == NULL) + return NULL; + + address -= map->start; + address += map->offset; + + bin_file = sp_elf_symbol_resolver_get_bin_file (self, map->filename); + + g_assert (bin_file != NULL); + + if (map->inode && !bin_file_check_inode (bin_file, map->inode)) + return g_strdup_printf ("%s: inode mismatch", map->filename); + + bin_sym = bin_file_lookup_symbol (bin_file, address); + bin_sym_name = bin_symbol_get_name (bin_file, bin_sym); + + if (map->filename) + *tag = guess_tag (self, map); + + return elf_demangle (bin_sym_name); +} + +static void +symbol_resolver_iface_init (SpSymbolResolverInterface *iface) +{ + iface->load = sp_elf_symbol_resolver_load; + iface->resolve = sp_elf_symbol_resolver_resolve; +} + +SpSymbolResolver * +sp_elf_symbol_resolver_new (void) +{ + return g_object_new (SP_TYPE_ELF_SYMBOL_RESOLVER, NULL); +} diff --git a/lib/sp-elf-symbol-resolver.h b/lib/sp-elf-symbol-resolver.h new file mode 100644 index 00000000..3b359af5 --- /dev/null +++ b/lib/sp-elf-symbol-resolver.h @@ -0,0 +1,34 @@ +/* sp-elf-symbol-resolver.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_ELF_SYMBOL_RESOLVER_H +#define SP_ELF_SYMBOL_RESOLVER_H + +#include "sp-symbol-resolver.h" + +G_BEGIN_DECLS + +#define SP_TYPE_ELF_SYMBOL_RESOLVER (sp_elf_symbol_resolver_get_type()) + +G_DECLARE_FINAL_TYPE (SpElfSymbolResolver, sp_elf_symbol_resolver, SP, ELF_SYMBOL_RESOLVER, GObject) + +SpSymbolResolver *sp_elf_symbol_resolver_new (void); + +G_END_DECLS + +#endif /* SP_ELF_SYMBOL_RESOLVER_H */ diff --git a/lib/sp-empty-state-view.c b/lib/sp-empty-state-view.c new file mode 100644 index 00000000..8164fdcd --- /dev/null +++ b/lib/sp-empty-state-view.c @@ -0,0 +1,41 @@ +/* sp-empty-state-view.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-empty-state-view.h" + +G_DEFINE_TYPE (SpEmptyStateView, sp_empty_state_view, GTK_TYPE_BIN) + +GtkWidget * +sp_empty_state_view_new (void) +{ + return g_object_new (SP_TYPE_EMPTY_STATE_VIEW, NULL); +} + +static void +sp_empty_state_view_class_init (SpEmptyStateViewClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sp-empty-state-view.ui"); +} + +static void +sp_empty_state_view_init (SpEmptyStateView *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/lib/sp-empty-state-view.h b/lib/sp-empty-state-view.h new file mode 100644 index 00000000..d9f79bbf --- /dev/null +++ b/lib/sp-empty-state-view.h @@ -0,0 +1,40 @@ +/* sp-empty-state-view.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_EMPTY_STATE_VIEW_H +#define SP_EMPTY_STATE_VIEW_H + +#include + +G_BEGIN_DECLS + +#define SP_TYPE_EMPTY_STATE_VIEW (sp_empty_state_view_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpEmptyStateView, sp_empty_state_view, SP, EMPTY_STATE_VIEW, GtkBin) + +struct _SpEmptyStateViewClass +{ + GtkBinClass parent; +}; + +GtkWidget *sp_empty_state_view_new (void); + +G_END_DECLS + +#endif /* SP_EMPTY_STATE_VIEW_H */ + diff --git a/lib/sp-error.c b/lib/sp-error.c new file mode 100644 index 00000000..5a3b44be --- /dev/null +++ b/lib/sp-error.c @@ -0,0 +1,30 @@ +/* sp-error.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-error.h" + +GQuark +sp_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("sp-error-quark"); + + return quark; +} diff --git a/lib/sp-error.h b/lib/sp-error.h new file mode 100644 index 00000000..ef8f1650 --- /dev/null +++ b/lib/sp-error.h @@ -0,0 +1,32 @@ +/* sp-error.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_ERROR_H +#define SP_ERROR_H + +#include + +G_BEGIN_DECLS + +#define SP_ERROR (sp_error_quark()) + +GQuark sp_error_quark (void); + +G_END_DECLS + +#endif /* SP_ERROR_H */ diff --git a/lib/sp-failed-state-view.c b/lib/sp-failed-state-view.c new file mode 100644 index 00000000..17021aaf --- /dev/null +++ b/lib/sp-failed-state-view.c @@ -0,0 +1,42 @@ +/* sp-failed-state-view.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-failed-state-view.h" + +G_DEFINE_TYPE (SpFailedStateView, sp_failed_state_view, GTK_TYPE_BIN) + +GtkWidget * +sp_failed_state_view_new (void) +{ + return g_object_new (SP_TYPE_FAILED_STATE_VIEW, NULL); +} + +static void +sp_failed_state_view_class_init (SpFailedStateViewClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/sysprof/ui/sp-failed-state-view.ui"); +} + +static void +sp_failed_state_view_init (SpFailedStateView *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/lib/sp-failed-state-view.h b/lib/sp-failed-state-view.h new file mode 100644 index 00000000..af4605b0 --- /dev/null +++ b/lib/sp-failed-state-view.h @@ -0,0 +1,43 @@ +/* sp-failed-state-view.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_FAILED_STATE_VIEW_H +#define SP_FAILED_STATE_VIEW_H + +#include + +#include "sp-profiler.h" + +G_BEGIN_DECLS + +#define SP_TYPE_FAILED_STATE_VIEW (sp_failed_state_view_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpFailedStateView, sp_failed_state_view, SP, FAILED_STATE_VIEW, GtkBin) + +struct _SpFailedStateViewClass +{ + GtkBinClass parent; +}; + +GtkWidget *sp_failed_state_view_new (void); +void sp_failed_state_view_set_profiler (SpFailedStateView *self, + SpProfiler *profiler); + +G_END_DECLS + +#endif /* SP_FAILED_STATE_VIEW_H */ diff --git a/lib/sp-gjs-source.c b/lib/sp-gjs-source.c new file mode 100644 index 00000000..ac163ec1 --- /dev/null +++ b/lib/sp-gjs-source.c @@ -0,0 +1,245 @@ +/* sp-gjs-source.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 + +#include "sp-capture-reader.h" +#include "sp-gjs-source.h" + +struct _SpGjsSource +{ + GObject parent_instance; + + SpCaptureWriter *writer; + GArray *pids; + GArray *enabled; +}; + +#define ENABLE_PROFILER 0x1 +#define DISABLE_PROFILER 0x0 + +static void source_iface_init (SpSourceInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpGjsSource, sp_gjs_source, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (SP_TYPE_SOURCE, source_iface_init)) + +static void +sp_gjs_source_finalize (GObject *object) +{ + SpGjsSource *self = (SpGjsSource *)object; + + g_clear_pointer (&self->pids, g_array_unref); + g_clear_pointer (&self->enabled, g_array_unref); + g_clear_pointer (&self->writer, sp_capture_writer_unref); + + G_OBJECT_CLASS (sp_gjs_source_parent_class)->finalize (object); +} + +static void +sp_gjs_source_class_init (SpGjsSourceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_gjs_source_finalize; +} + +static void +sp_gjs_source_init (SpGjsSource *self) +{ + self->pids = g_array_new (FALSE, FALSE, sizeof (GPid)); + self->enabled = g_array_new (FALSE, FALSE, sizeof (GPid)); +} + +static void +sp_gjs_source_process_capture (SpGjsSource *self, + GPid pid, + const gchar *path) +{ + g_autoptr(GError) error = NULL; + g_autoptr(SpCaptureReader) reader = NULL; + + g_assert (SP_IS_GJS_SOURCE (self)); + g_assert (self->writer != NULL); + g_assert (path != NULL); + + if (!(reader = sp_capture_reader_new (path, &error))) + { + g_warning ("Failed to load capture: %s", error->message); + return; + } + + if (!sp_capture_reader_splice (reader, self->writer, &error)) + { + g_warning ("Failed to load capture: %s", error->message); + return; + } +} + +static void +sp_gjs_source_process_captures (SpGjsSource *self) +{ + guint i; + + g_assert (SP_IS_GJS_SOURCE (self)); + g_assert (self->writer != NULL); + + for (i = 0; i < self->enabled->len; i++) + { + g_autofree gchar *filename = NULL; + g_autofree gchar *path = NULL; + GPid pid = g_array_index (self->enabled, GPid, i); + + filename = g_strdup_printf ("gjs-profile-%u", (guint)pid); + path = g_build_filename (g_get_tmp_dir (), filename, NULL); + + sp_gjs_source_process_capture (self, pid, path); + } +} + +static void +sp_gjs_source_set_writer (SpSource *source, + SpCaptureWriter *writer) +{ + SpGjsSource *self = (SpGjsSource *)source; + + g_assert (SP_IS_GJS_SOURCE (self)); + g_assert (writer != NULL); + + self->writer = sp_capture_writer_ref (writer); +} + +static gboolean +pid_is_profileable (GPid pid) +{ + g_autofree gchar *path = NULL; + g_autofree gchar *contents = NULL; + const gchar *libgjs; + gsize len = 0; + + g_assert (pid != -1); + + /* + * Make sure this process has linked in libgjs. No sense in sending it a + * signal unless we know it can handle it. + */ + + path = g_strdup_printf ("/proc/%d/maps", pid); + if (!g_file_get_contents (path, &contents, &len, NULL)) + return FALSE; + + if (NULL == (libgjs = strstr (contents, "libgjs."G_MODULE_SUFFIX))) + return FALSE; + + return TRUE; +} + +static void +sp_gjs_source_enable_pid (SpGjsSource *self, + GPid pid) +{ + union sigval si; + + g_assert (SP_IS_GJS_SOURCE (self)); + g_assert (pid != -1); + + si.sival_int = ENABLE_PROFILER; + + if (0 != sigqueue (pid, SIGUSR2, si)) + g_warning ("Failed to queue SIGUSR2 to pid %u", (guint)pid); + else + g_array_append_val (self->enabled, pid); +} + +static void +sp_gjs_source_disable_pid (SpGjsSource *self, + GPid pid) +{ + union sigval si; + + g_assert (SP_IS_GJS_SOURCE (self)); + g_assert (pid != -1); + + si.sival_int = DISABLE_PROFILER; + + if (0 != sigqueue (pid, SIGUSR2, si)) + g_warning ("Failed to queue SIGUSR2 to pid %u", (guint)pid); +} + +static void +sp_gjs_source_start (SpSource *source) +{ + SpGjsSource *self = (SpGjsSource *)source; + guint i; + + g_assert (SP_IS_GJS_SOURCE (self)); + + for (i = 0; i < self->pids->len; i++) + { + GPid pid = g_array_index (self->pids, GPid, i); + + if (pid_is_profileable (pid)) + sp_gjs_source_enable_pid (self, pid); + } +} + +static void +sp_gjs_source_stop (SpSource *source) +{ + SpGjsSource *self = (SpGjsSource *)source; + guint i; + + g_assert (SP_IS_GJS_SOURCE (self)); + + for (i = 0; i < self->pids->len; i++) + { + GPid pid = g_array_index (self->pids, GPid, i); + + if (pid_is_profileable (pid)) + sp_gjs_source_disable_pid (self, pid); + } + + sp_gjs_source_process_captures (self); +} + +static void +sp_gjs_source_add_pid (SpSource *source, + GPid pid) +{ + SpGjsSource *self = (SpGjsSource *)source; + + g_assert (SP_IS_GJS_SOURCE (self)); + g_assert (pid > -1); + + g_array_append_val (self->pids, pid); +} + +static void +source_iface_init (SpSourceInterface *iface) +{ + iface->set_writer = sp_gjs_source_set_writer; + iface->start = sp_gjs_source_start; + iface->stop = sp_gjs_source_stop; + iface->add_pid = sp_gjs_source_add_pid; +} + +SpSource * +sp_gjs_source_new (void) +{ + return g_object_new (SP_TYPE_GJS_SOURCE, NULL); +} diff --git a/lib/sp-gjs-source.h b/lib/sp-gjs-source.h new file mode 100644 index 00000000..b0d106d2 --- /dev/null +++ b/lib/sp-gjs-source.h @@ -0,0 +1,34 @@ +/* sp-gjs-source.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_GJS_SOURCE_H +#define SP_GJS_SOURCE_H + +#include "sp-source.h" + +G_BEGIN_DECLS + +#define SP_TYPE_GJS_SOURCE (sp_gjs_source_get_type()) + +G_DECLARE_FINAL_TYPE (SpGjsSource, sp_gjs_source, SP, GJS_SOURCE, GObject) + +SpSource *sp_gjs_source_new (void); + +G_END_DECLS + +#endif /* SP_GJS_SOURCE_H */ diff --git a/lib/sp-jitmap-symbol-resolver.c b/lib/sp-jitmap-symbol-resolver.c new file mode 100644 index 00000000..f2b80a70 --- /dev/null +++ b/lib/sp-jitmap-symbol-resolver.c @@ -0,0 +1,121 @@ +/* sp-jitmap-symbol-resolver.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-kernel-symbol.h" +#include "sp-jitmap-symbol-resolver.h" + +struct _SpJitmapSymbolResolver +{ + GObject parent_instance; + GHashTable *jitmap; +}; + +static void symbol_resolver_iface_init (SpSymbolResolverInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpJitmapSymbolResolver, + sp_jitmap_symbol_resolver, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE (SP_TYPE_SYMBOL_RESOLVER, + symbol_resolver_iface_init)) + +static void +sp_jitmap_symbol_resolver_finalize (GObject *object) +{ + SpJitmapSymbolResolver *self = (SpJitmapSymbolResolver *)object; + + g_clear_pointer (&self->jitmap, g_hash_table_unref); + + G_OBJECT_CLASS (sp_jitmap_symbol_resolver_parent_class)->finalize (object); +} + +static void +sp_jitmap_symbol_resolver_class_init (SpJitmapSymbolResolverClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_jitmap_symbol_resolver_finalize; +} + +static void +sp_jitmap_symbol_resolver_init (SpJitmapSymbolResolver *self) +{ + self->jitmap = g_hash_table_new_full (NULL, NULL, NULL, g_free); +} + +static void +sp_jitmap_symbol_resolver_load (SpSymbolResolver *resolver, + SpCaptureReader *reader) +{ + SpJitmapSymbolResolver *self = (SpJitmapSymbolResolver *)resolver; + SpCaptureFrameType type; + + g_assert (SP_IS_JITMAP_SYMBOL_RESOLVER (self)); + g_assert (reader != NULL); + + while (sp_capture_reader_peek_type (reader, &type)) + { + g_autoptr(GHashTable) jitmap = NULL; + GHashTableIter iter; + SpCaptureAddress addr; + const gchar *str; + + if (type != SP_CAPTURE_FRAME_JITMAP) + { + if (!sp_capture_reader_skip (reader)) + return; + continue; + } + + if (!(jitmap = sp_capture_reader_read_jitmap (reader))) + return; + + g_hash_table_iter_init (&iter, jitmap); + while (g_hash_table_iter_next (&iter, (gpointer *)&addr, (gpointer *)&str)) + g_hash_table_insert (self->jitmap, GSIZE_TO_POINTER (addr), g_strdup (str)); + } +} + +static gchar * +sp_jitmap_symbol_resolver_resolve (SpSymbolResolver *resolver, + guint64 time, + GPid pid, + SpCaptureAddress address, + GQuark *tag) +{ + SpJitmapSymbolResolver *self = (SpJitmapSymbolResolver *)resolver; + + g_assert (SP_IS_JITMAP_SYMBOL_RESOLVER (self)); + + *tag = 0; + + return g_strdup (g_hash_table_lookup (self->jitmap, GSIZE_TO_POINTER (address))); +} + +static void +symbol_resolver_iface_init (SpSymbolResolverInterface *iface) +{ + iface->load = sp_jitmap_symbol_resolver_load; + iface->resolve = sp_jitmap_symbol_resolver_resolve; +} + +SpSymbolResolver * +sp_jitmap_symbol_resolver_new (void) +{ + return g_object_new (SP_TYPE_JITMAP_SYMBOL_RESOLVER, NULL); +} diff --git a/lib/sp-jitmap-symbol-resolver.h b/lib/sp-jitmap-symbol-resolver.h new file mode 100644 index 00000000..1d07fc7e --- /dev/null +++ b/lib/sp-jitmap-symbol-resolver.h @@ -0,0 +1,34 @@ +/* sp-jitmap-symbol-resolver.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_JITMAP_SYMBOL_RESOLVER_H +#define SP_JITMAP_SYMBOL_RESOLVER_H + +#include "sp-symbol-resolver.h" + +G_BEGIN_DECLS + +#define SP_TYPE_JITMAP_SYMBOL_RESOLVER (sp_jitmap_symbol_resolver_get_type()) + +G_DECLARE_FINAL_TYPE (SpJitmapSymbolResolver, sp_jitmap_symbol_resolver, SP, JITMAP_SYMBOL_RESOLVER, GObject) + +SpSymbolResolver *sp_jitmap_symbol_resolver_new (void); + +G_END_DECLS + +#endif /* SP_JITMAP_SYMBOL_RESOLVER_H */ diff --git a/lib/sp-kernel-symbol-resolver.c b/lib/sp-kernel-symbol-resolver.c new file mode 100644 index 00000000..ed7cc9f6 --- /dev/null +++ b/lib/sp-kernel-symbol-resolver.c @@ -0,0 +1,79 @@ +/* sp-kernel-symbol-resolver.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-kernel-symbol.h" +#include "sp-kernel-symbol-resolver.h" + +struct _SpKernelSymbolResolver +{ + GObject parent_instance; +}; + +static void symbol_resolver_iface_init (SpSymbolResolverInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpKernelSymbolResolver, + sp_kernel_symbol_resolver, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE (SP_TYPE_SYMBOL_RESOLVER, + symbol_resolver_iface_init)) + +static GQuark linux_quark; + +static void +sp_kernel_symbol_resolver_class_init (SpKernelSymbolResolverClass *klass) +{ + linux_quark = g_quark_from_static_string ("Linux"); +} + +static void +sp_kernel_symbol_resolver_init (SpKernelSymbolResolver *skernel) +{ +} + +static gchar * +sp_kernel_symbol_resolver_resolve (SpSymbolResolver *resolver, + guint64 time, + GPid pid, + SpCaptureAddress address, + GQuark *tag) +{ + const SpKernelSymbol *sym; + + g_assert (SP_IS_SYMBOL_RESOLVER (resolver)); + + if (NULL != (sym = sp_kernel_symbol_from_address (address))) + { + *tag = linux_quark; + return g_strdup (sym->name); + } + + return NULL; +} + +static void +symbol_resolver_iface_init (SpSymbolResolverInterface *iface) +{ + iface->resolve = sp_kernel_symbol_resolver_resolve; +} + +SpSymbolResolver * +sp_kernel_symbol_resolver_new (void) +{ + return g_object_new (SP_TYPE_KERNEL_SYMBOL_RESOLVER, NULL); +} diff --git a/lib/sp-kernel-symbol-resolver.h b/lib/sp-kernel-symbol-resolver.h new file mode 100644 index 00000000..d2bf3b73 --- /dev/null +++ b/lib/sp-kernel-symbol-resolver.h @@ -0,0 +1,34 @@ +/* sp-kernel-symbol-resolver.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_KERNEL_SYMBOL_RESOLVER_H +#define SP_KERNEL_SYMBOL_RESOLVER_H + +#include "sp-symbol-resolver.h" + +G_BEGIN_DECLS + +#define SP_TYPE_KERNEL_SYMBOL_RESOLVER (sp_kernel_symbol_resolver_get_type()) + +G_DECLARE_FINAL_TYPE (SpKernelSymbolResolver, sp_kernel_symbol_resolver, SP, KERNEL_SYMBOL_RESOLVER, GObject) + +SpSymbolResolver *sp_kernel_symbol_resolver_new (void); + +G_END_DECLS + +#endif /* SP_KERNEL_SYMBOL_RESOLVER_H */ diff --git a/lib/sp-kernel-symbol.c b/lib/sp-kernel-symbol.c new file mode 100644 index 00000000..2855b296 --- /dev/null +++ b/lib/sp-kernel-symbol.c @@ -0,0 +1,205 @@ +/* sp-kernel-symbol.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-line-reader.h" +#include "sp-kernel-symbol.h" + +static GArray *kernel_symbols; +static const gchar *kernel_symbols_skip[] = { + /* IRQ stack */ + "common_interrupt", + "apic_timer_interrupt", + "smp_apic_timer_interrupt", + "hrtimer_interrupt", + "__run_hrtimer", + "perf_swevent_hrtimer", + "perf_event_overflow", + "__perf_event_overflow", + "perf_prepare_sample", + "perf_callchain", + "perf_swcounter_hrtimer", + "perf_counter_overflow", + "__perf_counter_overflow", + "perf_counter_output", + + /* NMI stack */ + "nmi_stack_correct", + "do_nmi", + "notify_die", + "atomic_notifier_call_chain", + "notifier_call_chain", + "perf_event_nmi_handler", + "perf_counter_nmi_handler", + "intel_pmu_handle_irq", + "perf_event_overflow", + "perf_counter_overflow", + "__perf_event_overflow", + "perf_prepare_sample", + "perf_callchain", + + NULL +}; + +static gint +sp_kernel_symbol_compare (gconstpointer a, + gconstpointer b) +{ + const SpKernelSymbol *syma = a; + const SpKernelSymbol *symb = b; + + if (syma->address > symb->address) + return 1; + else if (syma->address == symb->address) + return 0; + else + return -1; +} + +static gboolean +sp_kernel_symbol_load (void) +{ + g_autofree gchar *contents = NULL; + g_autoptr(GArray) ar = NULL; + g_autoptr(GHashTable) skip = NULL; + g_autoptr(SpLineReader) reader = NULL; + const gchar *line; + gsize len; + guint i; + + skip = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; kernel_symbols_skip [i]; i++) + g_hash_table_insert (skip, (gchar *)kernel_symbols_skip [i], NULL); + + ar = g_array_new (FALSE, TRUE, sizeof (SpKernelSymbol)); + + if (!g_file_get_contents ("/proc/kallsyms", &contents, &len, NULL)) + { + g_warning ("/proc/kallsyms is missing, kernel symbols will not be available"); + return FALSE; + } + + reader = sp_line_reader_new (contents, len); + + while (NULL != (line = sp_line_reader_next (reader, &len))) + { + gchar **tokens; + + ((gchar *)line) [len] = '\0'; + + tokens = g_strsplit_set (line, " \t", -1); + + if (tokens [0] && tokens [1] && tokens [2]) + { + SpCaptureAddress address; + gchar *endptr; + + if (g_hash_table_contains (skip, tokens [2])) + continue; + + address = g_ascii_strtoull (tokens [0], &endptr, 16); + + if (*endptr == '\0' && + (g_str_equal (tokens [1], "T") || g_str_equal (tokens [1], "t"))) + { + SpKernelSymbol sym; + + sym.address = address; + sym.name = g_steal_pointer (&tokens [2]); + + g_array_append_val (ar, sym); + } + } + + g_strfreev (tokens); + } + + if (ar->len == 0) + return FALSE; + + g_array_sort (ar, sp_kernel_symbol_compare); + + kernel_symbols = g_steal_pointer (&ar); + + return TRUE; +} + +static const SpKernelSymbol * +sp_kernel_symbol_lookup (SpKernelSymbol *symbols, + SpCaptureAddress address, + guint first, + guint last) +{ + if (address >= symbols [last].address) + { + return &symbols [last]; + } + else if (last - first < 3) + { + while (last >= first) + { + if (address >= symbols[last].address) + return &symbols [last]; + + last--; + } + + return NULL; + } + else + { + int mid = (first + last) / 2; + + if (symbols [mid].address > address) + return sp_kernel_symbol_lookup (symbols, address, first, mid); + else + return sp_kernel_symbol_lookup (symbols, address, mid, last); + } +} + +/** + * sp_kernel_symbol_from_address: + * @address: the address of the instruction pointer + * + * Locates the kernel symbol that contains @address. + * + * Returns: (transfer none): An #SpKernelSymbol or %NULL. + */ +const SpKernelSymbol * +sp_kernel_symbol_from_address (SpCaptureAddress address) +{ + const SpKernelSymbol *first; + + if (G_UNLIKELY (kernel_symbols == NULL)) + { + if (!sp_kernel_symbol_load ()) + return NULL; + } + + g_assert (kernel_symbols != NULL); + g_assert (kernel_symbols->len > 0); + + /* Short circuit if this is out of range */ + first = &g_array_index (kernel_symbols, SpKernelSymbol, 0); + if (address < first->address) + return NULL; + + return sp_kernel_symbol_lookup ((SpKernelSymbol *)kernel_symbols->data, + address, + 0, + kernel_symbols->len - 1); +} diff --git a/lib/sp-kernel-symbol.h b/lib/sp-kernel-symbol.h new file mode 100644 index 00000000..e5e42168 --- /dev/null +++ b/lib/sp-kernel-symbol.h @@ -0,0 +1,36 @@ +/* sp-kernel-symbol.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_KERNEL_SYMBOL_H +#define SP_KERNEL_SYMBOL_H + +#include "sp-capture-types.h" + +G_BEGIN_DECLS + +typedef struct +{ + SpCaptureAddress address; + const gchar *name; +} SpKernelSymbol; + +const SpKernelSymbol *sp_kernel_symbol_from_address (SpCaptureAddress address); + +G_END_DECLS + +#endif /* SP_KERNEL_SYMBOL_H */ diff --git a/lib/sp-line-reader.c b/lib/sp-line-reader.c new file mode 100644 index 00000000..174ed98a --- /dev/null +++ b/lib/sp-line-reader.c @@ -0,0 +1,124 @@ +/* sp-line-reader.c + * + * Copyright (C) 2015 Christian Hergert + * + * 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 "sp-line-reader.h" + +struct _SpLineReader +{ + const gchar *contents; + gsize length; + gsize pos; +}; + +void +sp_line_reader_free (SpLineReader *self) +{ + g_slice_free (SpLineReader, self); +} + +/** + * sp_line_reader_new: + * @contents: The buffer to read lines from + * @length: the length of @buffer in bytes + * + * Creates a new #SpLineReader for the contents provided. @contents are not + * copied and therefore it is a programming error to free contents before + * freeing the #SpLineReader structure. + * + * Use sp_line_reader_next() to read through the lines of the buffer. + * + * Returns: (transfer full): A new #SpLineReader that should be freed with + * sp_line_reader_free() when no longer in use. + */ +SpLineReader * +sp_line_reader_new (const gchar *contents, + gssize length) +{ + SpLineReader *self; + + g_return_val_if_fail (contents != NULL, NULL); + + self = g_slice_new (SpLineReader); + + if (length < 0) + length = strlen (contents); + + if (contents != NULL) + { + self->contents = contents; + self->length = length; + self->pos = 0; + } + else + { + self->contents = NULL; + self->length = 0; + self->pos = 0; + } + + return self; +} + +/** + * sp_line_reader_next: + * @self: the #SpLineReader + * @length: a location for the length of the line in bytes + * + * Moves forward to the beginning of the next line in the buffer. No changes to + * the buffer are made, and the result is a pointer within the string passed as + * @contents in sp_line_reader_init(). Since the line most likely will not be + * terminated with a NULL byte, you must provide @length to determine the + * length of the line. + * + * Using "line[length]" will place you on the \n that was found for the line. + * However, to perform this safely, you need to know that your string was + * either \0 terminated to begin with, or that your buffer provides enough space + * to guarantee you can dereference past the last "textual content" of the + * buffer. + * + * Returns: (nullable) (transfer none): The beginning of the line within the buffer + */ +const gchar * +sp_line_reader_next (SpLineReader *self, + gsize *length) +{ + const gchar *ret; + const gchar *endptr; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (length != NULL, NULL); + + if ((self->contents == NULL) || (self->pos >= self->length)) + { + *length = 0; + return NULL; + } + + ret = &self->contents [self->pos]; + + endptr = memchr (ret, '\n', self->length - self->pos); + if (G_UNLIKELY (endptr == NULL)) + endptr = &self->contents [self->length]; + + *length = (endptr - ret); + self->pos += *length + 1; + + return ret; +} diff --git a/lib/sp-line-reader.h b/lib/sp-line-reader.h new file mode 100644 index 00000000..c95f54ac --- /dev/null +++ b/lib/sp-line-reader.h @@ -0,0 +1,38 @@ +/* sp-line-reader.h + * + * Copyright (C) 2015 Christian Hergert + * + * 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 SP_LINE_READER_H +#define SP_LINE_READER_H + +#include + +G_BEGIN_DECLS + +typedef struct _SpLineReader SpLineReader; + +SpLineReader *sp_line_reader_new (const gchar *contents, + gssize length); +void sp_line_reader_free (SpLineReader *self); +const gchar *sp_line_reader_next (SpLineReader *self, + gsize *length); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpLineReader, sp_line_reader_free) + +G_END_DECLS + +#endif /* SP_LINE_READER_H */ diff --git a/lib/sp-map-lookaside.c b/lib/sp-map-lookaside.c new file mode 100644 index 00000000..ba590f4d --- /dev/null +++ b/lib/sp-map-lookaside.c @@ -0,0 +1,119 @@ +/* sp-map-lookaside.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-map-lookaside.h" + +struct _SpMapLookaside +{ + GSequence *seq; + GStringChunk *chunk; +}; + +static gint +sp_map_compare (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const SpMap *map_a = a; + const SpMap *map_b = b; + + return sp_capture_address_compare (map_a->start, map_b->start); +} + +static gint +sp_map_compare_in_range (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const SpMap *map_a = a; + const SpMap *map_b = b; + + /* + * map_b is the needle for the search. + * Only map_b->start is set. + */ + + if ((map_b->start >= map_a->start) && (map_b->start < map_a->end)) + return 0; + + return sp_capture_address_compare (map_a->start, map_b->start); +} + +static void +sp_map_free (gpointer data) +{ + SpMap *map = data; + + g_slice_free (SpMap, map); +} + +SpMapLookaside * +sp_map_lookaside_new (void) +{ + SpMapLookaside *ret; + + ret = g_slice_new (SpMapLookaside); + ret->seq = g_sequence_new (sp_map_free); + ret->chunk = g_string_chunk_new (4096); + + return ret; +} + +void +sp_map_lookaside_free (SpMapLookaside *self) +{ + g_sequence_free (self->seq); + g_string_chunk_free (self->chunk); + g_slice_free (SpMapLookaside, self); +} + +void +sp_map_lookaside_insert (SpMapLookaside *self, + const SpMap *map) +{ + SpMap *copy; + + g_assert (self != NULL); + g_assert (map != NULL); + + copy = g_slice_new (SpMap); + copy->start = map->start; + copy->end = map->end; + copy->offset = map->offset; + copy->inode = map->inode; + copy->filename = g_string_chunk_insert_const (self->chunk, map->filename); + + g_sequence_insert_sorted (self->seq, copy, sp_map_compare, NULL); +} + +const SpMap * +sp_map_lookaside_lookup (SpMapLookaside *self, + SpCaptureAddress address) +{ + SpMap map = { address }; + GSequenceIter *iter; + + g_assert (self != NULL); + + iter = g_sequence_lookup (self->seq, &map, sp_map_compare_in_range, NULL); + + if (iter != NULL) + return g_sequence_get (iter); + + return NULL; +} diff --git a/lib/sp-map-lookaside.h b/lib/sp-map-lookaside.h new file mode 100644 index 00000000..c106158b --- /dev/null +++ b/lib/sp-map-lookaside.h @@ -0,0 +1,50 @@ +/* sp-map-lookaside.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_MAP_LOOKASIDE_H +#define SP_MAP_LOOKASIDE_H + +#include + +#include "sp-capture-types.h" + +G_BEGIN_DECLS + +typedef struct _SpMapLookaside SpMapLookaside; + +typedef struct +{ + SpCaptureAddress start; + SpCaptureAddress end; + off_t offset; + ino_t inode; + const gchar *filename; +} SpMap; + +SpMapLookaside *sp_map_lookaside_new (void); +void sp_map_lookaside_insert (SpMapLookaside *self, + const SpMap *map); +const SpMap *sp_map_lookaside_lookup (SpMapLookaside *self, + SpCaptureAddress address); +void sp_map_lookaside_free (SpMapLookaside *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpMapLookaside, sp_map_lookaside_free) + +G_END_DECLS + +#endif /* SP_MAP_LOOKASIDE_H */ diff --git a/lib/sp-model-filter.c b/lib/sp-model-filter.c new file mode 100644 index 00000000..9b83c15c --- /dev/null +++ b/lib/sp-model-filter.c @@ -0,0 +1,472 @@ +/* sp-model-filter.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-model-filter.h" + +/* + * This is a simple model filter for GListModel. + * + * Let me start by saying how it works, and then how I wish it worked. + * + * This uses 2 GSequence (Treaps) to track the filters. One matches the + * underlying listmodel. One matches the filtered set. We hold onto our + * own reference to the object in the child list model, and update things + * as necessary when ::items-changed is emitted. + * + * I'd rather see this solved in one of two ways. + * + * 1) Add filtering support to GListStore + * + * or + * + * 2) Create a multi-tree data-structure that contains two tree nodes + * in each element. One tree contains all items, one tree contains + * the visible items (a subset of the other tree). The nodes might + * look something like: + * + * Item { + * TreeNode all_tree; + * TreeNode visible_tree; + * GObject *item; + * } + * + * But either way, this gets the job done for now. I'd venture a guess + * that in many cases (small lists), this is actually slower than just + * rechecking a simple GPtrArray, but let's see how it goes. + * + * -- Christian + */ + +typedef struct +{ + GListModel *child_model; + + GSequence *seq; + GSequence *visible_seq; + + SpModelFilterFunc filter_func; + gpointer filter_func_data; + GDestroyNotify filter_func_data_destroy; + + guint needs_rebuild : 1; +} SpModelFilterPrivate; + +typedef struct +{ + GSequenceIter *iter; + GObject *object; +} Element; + +static void list_model_iface_init (GListModelInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpModelFilter, sp_model_filter, G_TYPE_OBJECT, 0, + G_ADD_PRIVATE (SpModelFilter) + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, + list_model_iface_init)) + +enum { + PROP_0, + PROP_CHILD_MODEL, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +element_free (gpointer data) +{ + Element *e = data; + + g_clear_object (&e->object); + g_slice_free (Element, e); +} + +static gboolean +sp_model_filter_default_filter_func (GObject *item, + gpointer user_data) +{ + return TRUE; +} + +static void +sp_model_filter_child_model_items_changed (SpModelFilter *self, + guint position, + guint n_removed, + guint n_added, + GListModel *child_model) +{ + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + GSequenceIter *insert_before = NULL; + GSequenceIter *insert_iter; + GSequenceIter *lower; + GSequenceIter *upper; + guint i; + + g_assert (SP_IS_MODEL_FILTER (self)); + g_assert (G_IS_LIST_MODEL (child_model)); + + + for (i = 0; i < n_removed; i++) + { + GSequenceIter *iter; + Element *ele; + + iter = g_sequence_get_iter_at_pos (priv->seq, position); + ele = g_sequence_get (iter); + + if (ele->iter) + { + guint visible_position = g_sequence_iter_get_position (ele->iter); + + insert_before = g_sequence_iter_next (ele->iter); + g_sequence_remove (ele->iter); + g_list_model_items_changed (G_LIST_MODEL (self), visible_position, 1, 0); + } + + g_sequence_remove (iter); + } + + insert_iter = g_sequence_get_iter_at_pos (priv->seq, position + 1); + + if (insert_before != NULL) + goto add_items; + +#if GLIB_CHECK_VERSION(2, 48, 0) + if (g_sequence_is_empty (priv->visible_seq)) +#else + if (g_sequence_get_begin_iter (priv->visible_seq) == g_sequence_get_end_iter (priv->visible_seq)) +#endif + { + insert_before = g_sequence_get_end_iter (priv->visible_seq); + goto add_items; + } + + lower = g_sequence_get_begin_iter (priv->visible_seq); + upper = g_sequence_get_end_iter (priv->visible_seq); + + while (lower != upper) + { + GSequenceIter *mid; + GSequenceIter *iter; + guint mid_pos; + + mid = g_sequence_range_get_midpoint (lower, upper); + iter = g_sequence_get (mid); + mid_pos = g_sequence_iter_get_position (iter); + + if (mid_pos < position) + lower = g_sequence_iter_next (mid); + else if (mid_pos > position) + upper = g_sequence_iter_prev (mid); + else + upper = lower = mid; + } + + if (upper == g_sequence_get_end_iter (priv->visible_seq)) + insert_before = upper; + else + insert_before = + ((guint)g_sequence_iter_get_position (g_sequence_get (upper)) <= position) + ? upper : g_sequence_iter_next (upper); + +add_items: + for (i = 0; i < n_added; i++) + { + GSequenceIter *iter; + Element *ele; + + ele = g_slice_new (Element); + ele->object = g_list_model_get_item (priv->child_model, position + i); + ele->iter = NULL; + + iter = g_sequence_insert_before (insert_iter, ele); + + if (priv->filter_func (ele->object, priv->filter_func_data)) + { + ele->iter = g_sequence_insert_before (insert_before, iter); + g_list_model_items_changed (G_LIST_MODEL (self), + g_sequence_iter_get_position (ele->iter), + 0, 1); + } + } +} + +static void +sp_model_filter_finalize (GObject *object) +{ + SpModelFilter *self = (SpModelFilter *)object; + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + + g_clear_pointer (&priv->seq, g_sequence_free); + g_clear_pointer (&priv->visible_seq, g_sequence_free); + + if (priv->filter_func_data_destroy) + { + g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy); + priv->filter_func_data_destroy = NULL; + } + + g_clear_object (&priv->child_model); + + G_OBJECT_CLASS (sp_model_filter_parent_class)->finalize (object); +} + +static void +sp_model_filter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpModelFilter *self = SP_MODEL_FILTER (object); + + switch (prop_id) + { + case PROP_CHILD_MODEL: + g_value_set_object (value, sp_model_filter_get_child_model (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_model_filter_class_init (SpModelFilterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_model_filter_finalize; + object_class->get_property = sp_model_filter_get_property; + + properties [PROP_CHILD_MODEL] = + g_param_spec_object ("child-model", + "Child Model", + "The child model being filtered.", + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sp_model_filter_init (SpModelFilter *self) +{ + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + + priv->filter_func = sp_model_filter_default_filter_func; + priv->seq = g_sequence_new (element_free); + priv->visible_seq = g_sequence_new (NULL); + + priv->needs_rebuild = TRUE; +} + +static void +sp_model_filter_rebuild (SpModelFilter *self, + gboolean no_emit) +{ + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + guint new_n_items = 0; + guint old_n_items; + guint n_items; + guint i; + + g_assert (SP_IS_MODEL_FILTER (self)); + g_assert (priv->needs_rebuild); + + old_n_items = g_sequence_get_length (priv->visible_seq); + + g_clear_pointer (&priv->seq, g_sequence_free); + g_clear_pointer (&priv->visible_seq, g_sequence_free); + + priv->seq = g_sequence_new (element_free); + priv->visible_seq = g_sequence_new (NULL); + + n_items = g_list_model_get_n_items (priv->child_model); + + for (i = 0; i < n_items; i++) + { + GSequenceIter *iter; + Element *ele; + + ele = g_slice_new (Element); + ele->object = g_list_model_get_item (priv->child_model, i); + ele->iter = NULL; + + iter = g_sequence_append (priv->seq, ele); + + if (priv->filter_func (ele->object, priv->filter_func_data)) + { + ele->iter = g_sequence_append (priv->visible_seq, iter); + new_n_items++; + } + } + + if (!no_emit) + g_list_model_items_changed (G_LIST_MODEL (self), 0, old_n_items, new_n_items); + + priv->needs_rebuild = FALSE; +} + +static GType +sp_model_filter_get_item_type (GListModel *model) +{ + SpModelFilter *self = (SpModelFilter *)model; + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + + g_assert (SP_IS_MODEL_FILTER (self)); + + return g_list_model_get_item_type (priv->child_model); +} + +static guint +sp_model_filter_get_n_items (GListModel *model) +{ + SpModelFilter *self = (SpModelFilter *)model; + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + + g_assert (SP_IS_MODEL_FILTER (self)); + + if (priv->needs_rebuild) + sp_model_filter_rebuild (self, TRUE); + + return g_sequence_get_length (priv->visible_seq); +} + +static gpointer +sp_model_filter_get_item (GListModel *model, + guint position) +{ + SpModelFilter *self = (SpModelFilter *)model; + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + GSequenceIter *iter; + Element *ele; + + g_assert (SP_IS_MODEL_FILTER (self)); + + if (priv->needs_rebuild) + sp_model_filter_rebuild (self, TRUE); + + iter = g_sequence_get_iter_at_pos (priv->visible_seq, position); + + if (!iter || g_sequence_iter_is_end (iter)) + { + g_warning ("invalid position for filter, filter is corrupt"); + return NULL; + } + + iter = g_sequence_get (iter); + + if (!iter || g_sequence_iter_is_end (iter)) + { + g_warning ("invalid position for filter, filter is corrupt"); + return NULL; + } + + ele = g_sequence_get (iter); + + return g_object_ref (ele->object); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sp_model_filter_get_item_type; + iface->get_n_items = sp_model_filter_get_n_items; + iface->get_item = sp_model_filter_get_item; +} + +SpModelFilter * +sp_model_filter_new (GListModel *child_model) +{ + SpModelFilter *ret; + SpModelFilterPrivate *priv; + + g_return_val_if_fail (G_IS_LIST_MODEL (child_model), NULL); + + ret = g_object_new (SP_TYPE_MODEL_FILTER, NULL); + priv = sp_model_filter_get_instance_private (ret); + priv->child_model = g_object_ref (child_model); + + g_signal_connect_object (child_model, + "items-changed", + G_CALLBACK (sp_model_filter_child_model_items_changed), + ret, + G_CONNECT_SWAPPED); + + return ret; +} + +/** + * sp_model_filter_get_child_model: + * @self: A #SpModelFilter + * + * Gets the child model that is being filtered. + * + * Returns: (transfer none): A #GListModel. + */ +GListModel * +sp_model_filter_get_child_model (SpModelFilter *self) +{ + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + + g_return_val_if_fail (SP_IS_MODEL_FILTER (self), NULL); + + return priv->child_model; +} + +void +sp_model_filter_invalidate (SpModelFilter *self) +{ + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + + g_return_if_fail (SP_IS_MODEL_FILTER (self)); + + priv->needs_rebuild = TRUE; + + sp_model_filter_rebuild (self, FALSE); +} + +void +sp_model_filter_set_filter_func (SpModelFilter *self, + SpModelFilterFunc filter_func, + gpointer filter_func_data, + GDestroyNotify filter_func_data_destroy) +{ + SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self); + + g_return_if_fail (SP_IS_MODEL_FILTER (self)); + g_return_if_fail (filter_func || (!filter_func_data && !filter_func_data_destroy)); + + if (priv->filter_func_data_destroy) + g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy); + + if (filter_func != NULL) + { + priv->filter_func = filter_func; + priv->filter_func_data = filter_func_data; + priv->filter_func_data_destroy = filter_func_data_destroy; + } + else + { + priv->filter_func = sp_model_filter_default_filter_func; + priv->filter_func_data = NULL; + priv->filter_func_data_destroy = NULL; + } + + sp_model_filter_invalidate (self); +} diff --git a/lib/sp-model-filter.h b/lib/sp-model-filter.h new file mode 100644 index 00000000..1a6038b2 --- /dev/null +++ b/lib/sp-model-filter.h @@ -0,0 +1,48 @@ +/* sp-model-filter.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_MODEL_FILTER_H +#define SP_MODEL_FILTER_H + +#include + +G_BEGIN_DECLS + +#define SP_TYPE_MODEL_FILTER (sp_model_filter_get_type()) + +typedef gboolean (*SpModelFilterFunc) (GObject *object, + gpointer user_data); + +G_DECLARE_DERIVABLE_TYPE (SpModelFilter, sp_model_filter, SP, MODEL_FILTER, GObject) + +struct _SpModelFilterClass +{ + GObjectClass parent_class; +}; + +SpModelFilter *sp_model_filter_new (GListModel *child_model); +GListModel *sp_model_filter_get_child_model (SpModelFilter *self); +void sp_model_filter_invalidate (SpModelFilter *self); +void sp_model_filter_set_filter_func (SpModelFilter *self, + SpModelFilterFunc filter_func, + gpointer filter_func_data, + GDestroyNotify filter_func_data_destroy); + +G_END_DECLS + +#endif /* SP_MODEL_FILTER_H */ diff --git a/lib/sp-perf-counter.c b/lib/sp-perf-counter.c new file mode 100644 index 00000000..d400d5f6 --- /dev/null +++ b/lib/sp-perf-counter.c @@ -0,0 +1,671 @@ +/* sp-perf-counter.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 . + */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, Red Hat, Inc. + * Copyright 2004, 2005, Soeren Sandmann + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sp-perf-counter.h" + +#include "util.h" + +/* + * Number of pages to map for the ring buffer. We map one additional buffer + * at the beginning for header information to communicate with perf. + */ +#define N_PAGES 32 + +/* + * This represents a stream coming to us from perf. All SpPerfCounterInfo + * share a single GSource used for watching incoming G_IO_IN requests. + * The map is the mmap() zone we are using as a ring buffer for communicating + * with perf. The rest is for managing the ring buffer. + */ +typedef struct +{ + gint fd; + gpointer fdtag; + struct perf_event_mmap_page *map; + guint8 *data; + guint64 tail; + gint cpu; + guint in_callback : 1; +} SpPerfCounterInfo; + +struct _SpPerfCounter +{ + volatile gint ref_count; + + /* + * If we are should currently be enabled. We allow calling + * multiple times and disabling when we reach zero. + */ + guint enabled; + + /* + * Our main context and source for delivering callbacks. + */ + GMainContext *context; + GSource *source; + + /* + * An array of SpPerfCounterInfo, indicating all of our open + * perf stream.s + */ + GPtrArray *info; + + /* + * The callback to execute for every discovered perf event. + */ + SpPerfCounterCallback callback; + gpointer callback_data; + GDestroyNotify callback_data_destroy; + + /* + * The number of samples we've recorded. + */ + guint64 n_samples; +}; + +typedef struct +{ + GSource source; + SpPerfCounter *counter; +} PerfGSource; + +G_DEFINE_BOXED_TYPE (SpPerfCounter, + sp_perf_counter, + (GBoxedCopyFunc)sp_perf_counter_ref, + (GBoxedFreeFunc)sp_perf_counter_unref) + +static gboolean +perf_gsource_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + return callback ? callback (user_data) : G_SOURCE_CONTINUE; +} + +static GSourceFuncs source_funcs = { + NULL, NULL, perf_gsource_dispatch, NULL +}; + +static void +sp_perf_counter_info_free (SpPerfCounterInfo *info) +{ + if (info->map) + { + gsize map_size; + + map_size = N_PAGES * getpagesize () + getpagesize (); + munmap (info->map, map_size); + + info->map = NULL; + info->data = NULL; + } + + if (info->fd != -1) + { + close (info->fd); + info->fd = 0; + } + + g_slice_free (SpPerfCounterInfo, info); +} + +static void +sp_perf_counter_finalize (SpPerfCounter *self) +{ + guint i; + + g_assert (self != NULL); + g_assert (self->ref_count == 0); + + for (i = 0; i < self->info->len; i++) + { + SpPerfCounterInfo *info = g_ptr_array_index (self->info, i); + + if (info->fdtag) + g_source_remove_unix_fd (self->source, info->fdtag); + + sp_perf_counter_info_free (info); + } + + if (self->callback_data_destroy) + self->callback_data_destroy (self->callback_data); + + g_clear_pointer (&self->source, g_source_destroy); + g_clear_pointer (&self->info, g_ptr_array_free); + g_clear_pointer (&self->context, g_main_context_unref); + g_slice_free (SpPerfCounter, self); +} + +void +sp_perf_counter_unref (SpPerfCounter *self) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (self->ref_count > 0); + + if (g_atomic_int_dec_and_test (&self->ref_count)) + sp_perf_counter_finalize (self); +} + +SpPerfCounter * +sp_perf_counter_ref (SpPerfCounter *self) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (self->ref_count > 0, NULL); + + g_atomic_int_inc (&self->ref_count); + + return self; +} + +static void +sp_perf_counter_flush (SpPerfCounter *self, + SpPerfCounterInfo *info) +{ + guint64 head; + guint64 tail; + guint64 n_bytes = N_PAGES * getpagesize (); + guint64 mask = n_bytes - 1; + + g_assert (self != NULL); + g_assert (info != NULL); + + tail = info->tail; + head = info->map->data_head; + + read_barrier (); + + if (head < tail) + tail = head; + + while ((head - tail) >= sizeof (struct perf_event_header)) + { + g_autofree guint8 *free_me = NULL; + struct perf_event_header *header; + guint8 buffer[4096]; + + /* Note that: + * + * - perf events are a multiple of 64 bits + * - the perf event header is 64 bits + * - the data area is a multiple of 64 bits + * + * which means there will always be space for one header, which means we + * can safely dereference the size field. + */ + header = (struct perf_event_header *)(info->data + (tail & mask)); + + if (header->size > head - tail) + { + /* The kernel did not generate a complete event. + * I don't think that can happen, but we may as well + * be paranoid. + */ + break; + } + + if (info->data + (tail & mask) + header->size > info->data + n_bytes) + { + gint n_before; + gint n_after; + guint8 *b; + + if (header->size > sizeof buffer) + free_me = b = g_malloc (header->size); + else + b = buffer; + + n_after = (tail & mask) + header->size - n_bytes; + n_before = header->size - n_after; + + memcpy (b, info->data + (tail & mask), n_before); + memcpy (b + n_before, info->data, n_after); + + header = (struct perf_event_header *)b; + } + + if (header->type == PERF_RECORD_SAMPLE) + self->n_samples++; + + if (self->callback != NULL) + { + info->in_callback = TRUE; + self->callback ((SpPerfCounterEvent *)header, info->cpu, self->callback_data); + info->in_callback = FALSE; + } + + tail += header->size; + } + + info->tail = tail; + info->map->data_tail = tail; +} + +static gboolean +sp_perf_counter_dispatch (gpointer user_data) +{ + SpPerfCounter *self = user_data; + guint i; + + g_assert (self != NULL); + g_assert (self->info != NULL); + + for (i = 0; i < self->info->len; i++) + { + SpPerfCounterInfo *info = g_ptr_array_index (self->info, i); + + sp_perf_counter_flush (self, info); + } + + return G_SOURCE_CONTINUE; +} + +static void +sp_perf_counter_enable_info (SpPerfCounter *self, + SpPerfCounterInfo *info) +{ + g_assert (self != NULL); + g_assert (info != NULL); + + if (0 != ioctl (info->fd, PERF_EVENT_IOC_ENABLE)) + g_warning ("Failed to enable counters"); + + g_source_modify_unix_fd (self->source, info->fdtag, G_IO_IN); +} + +SpPerfCounter * +sp_perf_counter_new (GMainContext *context) +{ + SpPerfCounter *ret; + + if (context == NULL) + context = g_main_context_default (); + + ret = g_slice_new0 (SpPerfCounter); + ret->ref_count = 1; + ret->info = g_ptr_array_new (); + ret->context = g_main_context_ref (context); + ret->source = g_source_new (&source_funcs, sizeof (PerfGSource)); + + ((PerfGSource *)ret->source)->counter = ret; + g_source_set_callback (ret->source, sp_perf_counter_dispatch, ret, NULL); + g_source_set_name (ret->source, "[perf]"); + g_source_attach (ret->source, context); + + return ret; +} + +void +sp_perf_counter_close (SpPerfCounter *self, + gint fd) +{ + guint i; + + g_return_if_fail (self != NULL); + g_return_if_fail (fd != -1); + + for (i = 0; i < self->info->len; i++) + { + SpPerfCounterInfo *info = g_ptr_array_index (self->info, i); + + if (info->fd == fd) + { + g_ptr_array_remove_index (self->info, i); + if (self->source) + g_source_remove_unix_fd (self->source, info->fdtag); + sp_perf_counter_info_free (info); + return; + } + } +} + +static void +sp_perf_counter_add_info (SpPerfCounter *self, + int fd, + int cpu) +{ + SpPerfCounterInfo *info; + guint8 *map; + gsize map_size; + + g_assert (self != NULL); + g_assert (fd != -1); + + map_size = N_PAGES * getpagesize () + getpagesize (); + map = mmap (NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (map == MAP_FAILED) + { + close (fd); + return; + } + + info = g_slice_new0 (SpPerfCounterInfo); + info->fd = fd; + info->map = (gpointer)map; + info->data = map + getpagesize (); + info->tail = 0; + info->cpu = cpu; + + g_ptr_array_add (self->info, info); + + info->fdtag = g_source_add_unix_fd (self->source, info->fd, G_IO_ERR); + + if (self->enabled) + sp_perf_counter_enable_info (self, info); +} + +void +sp_perf_counter_take_fd (SpPerfCounter *self, + int fd) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (fd > -1); + + sp_perf_counter_add_info (self, fd, -1); +} + +#ifdef ENABLE_SYSPROFD +static GDBusProxy * +get_proxy (void) +{ + static GDBusProxy *proxy; + GDBusConnection *bus = NULL; + + if (proxy != NULL) + return g_object_ref (proxy); + + bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); + if (bus == NULL) + return NULL; + + proxy = g_dbus_proxy_new_sync (bus, + (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION), + NULL, + "org.gnome.Sysprof2", + "/org/gnome/Sysprof2", + "org.gnome.Sysprof2", + NULL, NULL); + + if (proxy != NULL) + { + g_object_add_weak_pointer (G_OBJECT (proxy), (gpointer *)&proxy); + return g_object_ref (proxy); + } + + return NULL; +} + +static gboolean +authorize_proxy (GDBusProxy *proxy) +{ + PolkitSubject *subject = NULL; + GPermission *permission = NULL; + GDBusConnection *conn; + const gchar *name; + + g_assert (G_IS_DBUS_PROXY (proxy)); + + conn = g_dbus_proxy_get_connection (proxy); + if (conn == NULL) + goto failure; + + name = g_dbus_connection_get_unique_name (conn); + if (name == NULL) + goto failure; + + subject = polkit_system_bus_name_new (name); + if (subject == NULL) + goto failure; + + permission = polkit_permission_new_sync ("org.gnome.sysprof2.perf-event-open", subject, NULL, NULL); + if (permission == NULL) + goto failure; + + if (!g_permission_acquire (permission, NULL, NULL)) + goto failure; + + return TRUE; + +failure: + g_clear_object (&subject); + g_clear_object (&permission); + + return FALSE; +} + +static GDBusProxy * +get_authorized_proxy (void) +{ + g_autoptr(GDBusProxy) proxy = NULL; + + proxy = get_proxy (); + if (proxy != NULL && authorize_proxy (proxy)) + return g_steal_pointer (&proxy); + + return NULL; +} +#endif + +gint +sp_perf_counter_open (SpPerfCounter *self, + struct perf_event_attr *attr, + GPid pid, + gint cpu, + gint group_fd, + gulong flags) +{ +#ifdef ENABLE_SYSPROFD + g_autoptr(GError) error = NULL; + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GUnixFDList) fdlist = NULL; + g_autoptr(GVariant) res = NULL; + g_autoptr(GVariant) params = NULL; + gint handle = -1; +#endif + gint ret = -1; + + g_return_val_if_fail (self != NULL, -1); + g_return_val_if_fail (attr != NULL, -1); + + /* + * First, we try to run the syscall locally, since we should avoid the + * polkit request unless we have to use it for elevated privileges. + */ + if (-1 != (ret = syscall (__NR_perf_event_open, attr, pid, cpu, group_fd, flags))) + { + sp_perf_counter_take_fd (self, ret); + return ret; + } + +#ifdef ENABLE_SYSPROFD + params = g_variant_new_parsed ( + "(" + "[" + "{'comm', <%b>}," + "{'clockid', <%i>}," + "{'config', <%t>}," + "{'disabled', <%b>}," + "{'exclude_idle', <%b>}," + "{'mmap', <%b>}," + "{'wakeup_events', <%u>}," + "{'sample_period', <%t>}," + "{'sample_type', <%t>}," + "{'task', <%b>}," + "{'type', <%u>}," + "{'use_clockid', <%b>}" + "]," + "%i," + "%i," + "%t" + ")", + (gboolean)!!attr->comm, + (gint32)attr->clockid, + (guint64)attr->config, + (gboolean)!!attr->disabled, + (gboolean)!!attr->exclude_idle, + (gboolean)!!attr->mmap, + (guint32)attr->wakeup_events, + (guint64)attr->sample_period, + (guint64)attr->sample_type, + (gboolean)!!attr->task, + (guint32)attr->type, + (gboolean)!!attr->use_clockid, + (gint32)pid, + (gint32)cpu, + (guint64)flags); + + params = g_variant_ref_sink (params); + + if (NULL == (proxy = get_authorized_proxy ())) + { + errno = EPERM; + return -1; + } + + res = g_dbus_proxy_call_with_unix_fd_list_sync (proxy, + "PerfEventOpen", + params, + G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, + 60000, + NULL, + &fdlist, + NULL, + &error); + + if (res == NULL) + { + g_autofree gchar *str = g_variant_print (params, TRUE); + + g_warning ("PerfEventOpen: %s: %s", error->message, str); + return -1; + } + + if (!g_variant_is_of_type (res, (const GVariantType *)"(h)")) + { + g_warning ("Received something other than a handle"); + return -1; + } + + if (fdlist == NULL) + { + g_warning ("Failed to receive fdlist"); + return -1; + } + + g_variant_get (res, "(h)", &handle); + + if (-1 == (ret = g_unix_fd_list_get (fdlist, handle, &error))) + { + g_warning ("%s", error->message); + return -1; + } + + sp_perf_counter_take_fd (self, ret); +#endif + + return ret; +} + +void +sp_perf_counter_set_callback (SpPerfCounter *self, + SpPerfCounterCallback callback, + gpointer callback_data, + GDestroyNotify callback_data_destroy) +{ + g_return_if_fail (self != NULL); + + if (self->callback_data_destroy) + self->callback_data_destroy (self->callback_data); + + self->callback = callback; + self->callback_data = callback_data; + self->callback_data_destroy = callback_data_destroy; +} + +void +sp_perf_counter_enable (SpPerfCounter *self) +{ + g_return_if_fail (self != NULL); + + if (g_atomic_int_add (&self->enabled, 1) == 0) + { + guint i; + + for (i = 0; i < self->info->len; i++) + { + SpPerfCounterInfo *info = g_ptr_array_index (self->info, i); + + sp_perf_counter_enable_info (self, info); + } + } +} + +void +sp_perf_counter_disable (SpPerfCounter *self) +{ + g_return_if_fail (self != NULL); + + if (g_atomic_int_dec_and_test (&self->enabled)) + { + guint i; + + for (i = 0; i < self->info->len; i++) + { + SpPerfCounterInfo *info = g_ptr_array_index (self->info, i); + + if (0 != ioctl (info->fd, PERF_EVENT_IOC_DISABLE)) + g_warning ("Failed to disable counters"); + + if (!info->in_callback) + sp_perf_counter_flush (self, info); + + g_source_modify_unix_fd (self->source, info->fdtag, G_IO_ERR); + } + } +} diff --git a/lib/sp-perf-counter.h b/lib/sp-perf-counter.h new file mode 100644 index 00000000..d90f8b99 --- /dev/null +++ b/lib/sp-perf-counter.h @@ -0,0 +1,138 @@ +/* sp-perf-counter.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PERF_COUNTER_H +#define SP_PERF_COUNTER_H + +#include +#include + + +G_BEGIN_DECLS + +#define SP_TYPE_PERF_COUNTER (sp_perf_counter_get_type()) + +typedef struct _SpPerfCounter SpPerfCounter; + +#pragma pack(push, 1) + +typedef struct +{ + /* + * These fields are available as the suffix only because we have specified + * them when creating attributes. Be careful about using them. + * Ideally, we would probably switch from using structures overlaid with + * casts to a reader design, which knows about the attributes. + */ + guint32 pid, tid; + guint64 time; +} SpPerfCounterSuffix; + +typedef struct +{ + struct perf_event_header header; + guint32 pid; + guint32 ppid; + guint32 tid; + guint32 ptid; + guint64 time; +} SpPerfCounterEventFork; + +typedef struct +{ + struct perf_event_header header; + guint32 pid; + guint32 tid; + gchar comm[0]; +} SpPerfCounterEventComm; + +typedef struct +{ + struct perf_event_header header; + guint32 pid; + guint32 ppid; + guint32 tid; + guint32 ptid; + guint64 time; +} SpPerfCounterEventExit; + +typedef struct +{ + struct perf_event_header header; + guint32 pid; + guint32 tid; + guint64 addr; + guint64 len; + guint64 pgoff; + char filename[0]; +} SpPerfCounterEventMmap; + +typedef struct +{ + struct perf_event_header header; + guint64 ip; + guint32 pid; + guint32 tid; + guint64 time; + guint64 n_ips; + guint64 ips[0]; +} SpPerfCounterEventSample; + +typedef union +{ + struct perf_event_header header; + guint8 raw[0]; + SpPerfCounterEventFork fork; + SpPerfCounterEventComm comm; + SpPerfCounterEventExit exit; + SpPerfCounterEventMmap mmap; + SpPerfCounterEventSample sample; +} SpPerfCounterEvent; + +#pragma pack(pop) + +typedef void (*SpPerfCounterCallback) (SpPerfCounterEvent *event, + guint cpu, + gpointer user_data); + +GType sp_perf_counter_get_type (void); +SpPerfCounter *sp_perf_counter_new (GMainContext *context); +void sp_perf_counter_set_callback (SpPerfCounter *self, + SpPerfCounterCallback callback, + gpointer callback_data, + GDestroyNotify callback_data_destroy); +void sp_perf_counter_add_pid (SpPerfCounter *self, + GPid pid); +gint sp_perf_counter_open (SpPerfCounter *self, + struct perf_event_attr *attr, + GPid pid, + gint cpu, + gint group_fd, + gulong flags); +void sp_perf_counter_take_fd (SpPerfCounter *self, + int fd); +void sp_perf_counter_enable (SpPerfCounter *self); +void sp_perf_counter_disable (SpPerfCounter *self); +void sp_perf_counter_close (SpPerfCounter *self, + gint fd); +SpPerfCounter *sp_perf_counter_ref (SpPerfCounter *self); +void sp_perf_counter_unref (SpPerfCounter *self); + +G_END_DECLS + +#endif /* SP_PERF_COUNTER_H */ diff --git a/lib/sp-perf-source.c b/lib/sp-perf-source.c new file mode 100644 index 00000000..8a4f7540 --- /dev/null +++ b/lib/sp-perf-source.c @@ -0,0 +1,459 @@ +/* sp-perf-source.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 . + */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, Red Hat, Inc. + * Copyright 2004, 2005, Soeren Sandmann + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "sp-clock.h" +#include "sp-perf-counter.h" +#include "sp-perf-source.h" + +#define N_WAKEUP_EVENTS 149 + +struct _SpPerfSource +{ + GObject parent_instance; + + SpCaptureWriter *writer; + SpPerfCounter *counter; + GHashTable *pids; + + guint running : 1; +}; + +static void source_iface_init (SpSourceInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpPerfSource, sp_perf_source, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (SP_TYPE_SOURCE, source_iface_init)) + +enum { + TARGET_EXITED, + N_SIGNALS +}; + +static guint signals [N_SIGNALS]; + +static void +sp_perf_source_real_target_exited (SpPerfSource *self) +{ + g_assert (SP_IS_PERF_SOURCE (self)); + + sp_source_emit_finished (SP_SOURCE (self)); +} + +static void +sp_perf_source_finalize (GObject *object) +{ + SpPerfSource *self = (SpPerfSource *)object; + + g_clear_pointer (&self->writer, sp_capture_writer_unref); + g_clear_pointer (&self->counter, sp_perf_counter_unref); + g_clear_pointer (&self->pids, g_hash_table_unref); + + G_OBJECT_CLASS (sp_perf_source_parent_class)->finalize (object); +} + +static void +sp_perf_source_class_init (SpPerfSourceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_perf_source_finalize; + + signals [TARGET_EXITED] = + g_signal_new_class_handler ("target-exited", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_CALLBACK (sp_perf_source_real_target_exited), + NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +static void +sp_perf_source_init (SpPerfSource *self) +{ + self->pids = g_hash_table_new (NULL, NULL); +} + +static gboolean +do_emit_exited (gpointer data) +{ + g_autoptr(SpPerfSource) self = data; + + g_signal_emit (self, signals [TARGET_EXITED], 0); + + return G_SOURCE_REMOVE; +} + +static void +sp_perf_source_handle_sample (SpPerfSource *self, + gint cpu, + const SpPerfCounterEventSample *sample) +{ + const guint64 *ips; + gint n_ips; + guint64 trace[3]; + + g_assert (SP_IS_PERF_SOURCE (self)); + g_assert (sample != NULL); + + ips = sample->ips; + n_ips = sample->n_ips; + + if (n_ips == 0) + { + if (sample->header.misc & PERF_RECORD_MISC_KERNEL) + { + trace[0] = PERF_CONTEXT_KERNEL; + trace[1] = sample->ip; + trace[2] = PERF_CONTEXT_USER; + + ips = trace; + n_ips = 3; + } + else + { + trace[0] = PERF_CONTEXT_USER; + trace[1] = sample->ip; + + ips = trace; + n_ips = 2; + } + } + + sp_capture_writer_add_sample (self->writer, + sample->time, + cpu, + sample->pid, + ips, + n_ips); +} + +static void +sp_perf_source_handle_event (SpPerfCounterEvent *event, + guint cpu, + gpointer user_data) +{ + SpPerfSource *self = user_data; + SpPerfCounterSuffix *suffix; + + g_assert (SP_IS_PERF_SOURCE (self)); + g_assert (event != NULL); + + switch (event->header.type) + { + case PERF_RECORD_COMM: + suffix = (SpPerfCounterSuffix *)event->raw + + G_STRUCT_OFFSET (SpPerfCounterEventComm, comm) + + strlen (event->comm.comm) + + 1; + + sp_capture_writer_add_process (self->writer, + suffix->time, + cpu, + event->comm.pid, + event->comm.comm); + + break; + + case PERF_RECORD_EXIT: + sp_capture_writer_add_exit (self->writer, + event->exit.time, + cpu, + event->exit.pid); + + if (g_hash_table_contains (self->pids, GINT_TO_POINTER (event->exit.pid))) + { + g_hash_table_remove (self->pids, GINT_TO_POINTER (event->exit.pid)); + + if (self->running && (g_hash_table_size (self->pids) > 0)) + { + self->running = FALSE; + sp_perf_counter_disable (self->counter); + g_timeout_add (0, do_emit_exited, g_object_ref (self)); + } + } + + break; + + case PERF_RECORD_FORK: + sp_capture_writer_add_fork (self->writer, + event->fork.time, + cpu, + event->fork.ppid, + event->fork.pid); + + /* + * TODO: We should add support for "follow fork" of the GPid if we are + * targetting it. + */ + + break; + + case PERF_RECORD_LOST: + break; + + case PERF_RECORD_MMAP: + suffix = (SpPerfCounterSuffix *)event->raw + + G_STRUCT_OFFSET (SpPerfCounterEventMmap, filename) + + strlen (event->mmap.filename) + + 1; + + sp_capture_writer_add_map (self->writer, + suffix->time, + cpu, + event->mmap.pid, + event->mmap.addr, + event->mmap.addr + event->mmap.len, + event->mmap.pgoff, + 0, + event->mmap.filename); + + break; + + case PERF_RECORD_READ: + break; + + case PERF_RECORD_SAMPLE: + sp_perf_source_handle_sample (self, cpu, &event->sample); + break; + + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: + default: + break; + } +} + +static gboolean +sp_perf_source_start_pid (SpPerfSource *self, + GPid pid, + GError **error) +{ + struct perf_event_attr attr = { 0 }; + gulong flags = 0; + gint ncpu = g_get_num_processors (); + gint cpu = 0; + gint fd; + + g_assert (SP_IS_PERF_SOURCE (self)); + + attr.sample_type = PERF_SAMPLE_IP + | PERF_SAMPLE_TID + | PERF_SAMPLE_CALLCHAIN + | PERF_SAMPLE_TIME; + attr.wakeup_events = N_WAKEUP_EVENTS; + attr.disabled = TRUE; + attr.mmap = 1; + attr.comm = 1; + attr.task = 1; + attr.exclude_idle = 1; + attr.size = sizeof attr; + attr.clockid = sp_clock; + attr.use_clockid = 1; + + if (pid != -1) + { + ncpu = 0; + cpu = -1; + } + + for (; cpu < ncpu; cpu++) + { + attr.type = PERF_TYPE_HARDWARE; + attr.config = PERF_COUNT_HW_CPU_CYCLES; + attr.sample_period = 1200000; + + fd = sp_perf_counter_open (self->counter, &attr, pid, cpu, -1, flags); + + if (fd == -1) + { + /* + * We might just not have access to hardware counters, so try to + * gracefully fallback to software counters. + */ + attr.type = PERF_TYPE_SOFTWARE; + attr.config = PERF_COUNT_SW_CPU_CLOCK; + attr.sample_period = 1000000; + + errno = 0; + + fd = sp_perf_counter_open (self->counter, &attr, pid, cpu, -1, flags); + + if (fd == -1) + { + if (errno == EPERM || errno == EACCES) + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + _("Sysprof requires authorization to access your computers performance counters.")); + else + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("An error occurred while attempting to access performance counters: %s"), + g_strerror (errno)); + + sp_source_stop (SP_SOURCE (self)); + + return FALSE; + } + } + } + + return TRUE; +} + +static void +sp_perf_source_start (SpSource *source) +{ + SpPerfSource *self = (SpPerfSource *)source; + g_autoptr(GError) error = NULL; + + g_assert (SP_IS_PERF_SOURCE (self)); + + self->counter = sp_perf_counter_new (NULL); + + sp_perf_counter_set_callback (self->counter, + sp_perf_source_handle_event, + self, NULL); + + if (g_hash_table_size (self->pids) > 0) + { + GHashTableIter iter; + gpointer key; + + g_hash_table_iter_init (&iter, self->pids); + + while (g_hash_table_iter_next (&iter, &key, NULL)) + { + GPid pid = GPOINTER_TO_INT (key); + + if (!sp_perf_source_start_pid (self, pid, &error)) + { + sp_source_emit_failed (source, error); + return; + } + } + } + else + { + if (!sp_perf_source_start_pid (self, -1, &error)) + { + sp_source_emit_failed (source, error); + return; + } + } + + self->running = TRUE; + + sp_perf_counter_enable (self->counter); + + sp_source_emit_ready (source); +} + +static void +sp_perf_source_stop (SpSource *source) +{ + SpPerfSource *self = (SpPerfSource *)source; + + g_assert (SP_IS_PERF_SOURCE (self)); + + if (self->running) + { + self->running = FALSE; + sp_perf_counter_disable (self->counter); + } + + g_clear_pointer (&self->counter, sp_perf_counter_unref); + + sp_source_emit_finished (source); +} + +static void +sp_perf_source_set_writer (SpSource *source, + SpCaptureWriter *writer) +{ + SpPerfSource *self = (SpPerfSource *)source; + + g_assert (SP_IS_PERF_SOURCE (self)); + g_assert (writer != NULL); + + self->writer = sp_capture_writer_ref (writer); +} + +static void +sp_perf_source_add_pid (SpSource *source, + GPid pid) +{ + SpPerfSource *self = (SpPerfSource *)source; + + g_return_if_fail (SP_IS_PERF_SOURCE (self)); + g_return_if_fail (pid >= -1); + g_return_if_fail (self->writer == NULL); + + g_hash_table_add (self->pids, GINT_TO_POINTER (pid)); +} + +static void +source_iface_init (SpSourceInterface *iface) +{ + iface->start = sp_perf_source_start; + iface->stop = sp_perf_source_stop; + iface->set_writer = sp_perf_source_set_writer; + iface->add_pid = sp_perf_source_add_pid; +} + +SpSource * +sp_perf_source_new (void) +{ + return g_object_new (SP_TYPE_PERF_SOURCE, NULL); +} + +void +sp_perf_source_set_target_pid (SpPerfSource *self, + GPid pid) +{ + g_return_if_fail (SP_IS_PERF_SOURCE (self)); + g_return_if_fail (pid >= -1); + + if (pid == -1) + g_hash_table_remove_all (self->pids); + else + sp_perf_source_add_pid (SP_SOURCE (self), pid); +} diff --git a/lib/sp-perf-source.h b/lib/sp-perf-source.h new file mode 100644 index 00000000..864467b2 --- /dev/null +++ b/lib/sp-perf-source.h @@ -0,0 +1,36 @@ +/* sp-perf-source.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PERF_SOURCE_H +#define SP_PERF_SOURCE_H + +#include "sp-source.h" + +G_BEGIN_DECLS + +#define SP_TYPE_PERF_SOURCE (sp_perf_source_get_type()) + +G_DECLARE_FINAL_TYPE (SpPerfSource, sp_perf_source, SP, PERF_SOURCE, GObject) + +SpSource *sp_perf_source_new (void); +void sp_perf_source_set_target_pid (SpPerfSource *self, + GPid pid); + +G_END_DECLS + +#endif /* SP_PERF_SOURCE_H */ diff --git a/lib/sp-proc-source.c b/lib/sp-proc-source.c new file mode 100644 index 00000000..efe70932 --- /dev/null +++ b/lib/sp-proc-source.c @@ -0,0 +1,347 @@ +/* sp-proc-source.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 . + */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, Red Hat, Inc. + * Copyright 2004, 2005, Soeren Sandmann + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "sp-proc-source.h" + +struct _SpProcSource +{ + GObject parent_instance; + SpCaptureWriter *writer; + GArray *pids; +}; + +static void source_iface_init (SpSourceInterface *iface); +static gchar **proc_readlines (const gchar *format, + ...) + G_GNUC_PRINTF (1, 2); + +G_DEFINE_TYPE_EXTENDED (SpProcSource, sp_proc_source, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (SP_TYPE_SOURCE, source_iface_init)) + +static gchar ** +proc_readlines (const gchar *format, + ...) +{ + gchar **ret = NULL; + gchar *filename = NULL; + gchar *contents = NULL; + va_list args; + gsize len; + + g_assert (format != NULL); + + va_start (args, format); + filename = g_strdup_vprintf (format, args); + va_end (args); + + if (g_file_get_contents (filename, &contents, &len, NULL)) + ret = g_strsplit (contents, "\n", 0); + + g_free (contents); + g_free (filename); + + return ret; +} + +gchar * +sp_proc_source_get_command_line (GPid pid, + gboolean *is_kernel) +{ + gchar *ret; + gchar **lines; + + + if (is_kernel) + *is_kernel = FALSE; + + /* + * Get the full command line from /proc/pid/cmdline. + */ + if (NULL != (lines = proc_readlines ("/proc/%d/cmdline", pid))) + { + if (lines [0] && lines [0][0]) + { + ret = g_strdup (lines [0]); + g_strfreev (lines); + return ret; + } + + g_strfreev (lines); + } + + /* + * We are guessing this is a kernel process based on cmdline being null. + */ + if (is_kernel) + *is_kernel = TRUE; + + /* + * Check the first line of /proc/pid/status for Name: foo + */ + if (NULL != (lines = proc_readlines ("/proc/%d/status", pid))) + { + if (lines [0] && g_str_has_prefix (lines [0], "Name:")) + { + ret = g_strstrip (g_strdup (lines [0] + 5)); + g_strfreev (lines); + return ret; + } + + g_strfreev (lines); + } + + return NULL; +} + +static void +sp_proc_source_populate_process (SpProcSource *self, + GPid pid) +{ + gchar *cmdline; + + g_assert (SP_IS_PROC_SOURCE (self)); + g_assert (pid > 0); + + if (NULL != (cmdline = sp_proc_source_get_command_line (pid, NULL))) + { + sp_capture_writer_add_process (self->writer, + SP_CAPTURE_CURRENT_TIME, + -1, + pid, + cmdline); + g_free (cmdline); + } +} + +static void +sp_proc_source_populate_maps (SpProcSource *self, + GPid pid) +{ + g_auto(GStrv) lines = NULL; + guint i; + + g_assert (SP_IS_PROC_SOURCE (self)); + g_assert (pid > 0); + + if (NULL == (lines = proc_readlines ("/proc/%d/maps", pid))) + return; + + for (i = 0; lines [i] != NULL; i++) + { + gchar file[256]; + gulong start; + gulong end; + gulong offset; + gulong inode; + gint r; + + r = sscanf (lines [i], + "%lx-%lx %*15s %lx %*x:%*x %lu %255s", + &start, &end, &offset, &inode, file); + + file [sizeof file - 1] = '\0'; + + if (r != 5) + continue; + + if (strcmp ("[vdso]", file) == 0) + { + /* + * Søren Sandmann Pedersen says: + * + * For the vdso, the kernel reports 'offset' as the + * the same as the mapping addres. This doesn't make + * any sense to me, so we just zero it here. There + * is code in binfile.c (read_inode) that returns 0 + * for [vdso]. + */ + offset = 0; + inode = 0; + } + + sp_capture_writer_add_map (self->writer, + SP_CAPTURE_CURRENT_TIME, + -1, + pid, + start, + end, + offset, + inode, + file); + } +} + +static void +sp_proc_source_populate (SpProcSource *self) +{ + const gchar *name; + GDir *dir; + + g_assert (SP_IS_PROC_SOURCE (self)); + + if (self->pids->len > 0) + { + guint i; + + for (i = 0; i < self->pids->len; i++) + { + GPid pid = g_array_index (self->pids, GPid, i); + + sp_proc_source_populate_process (self, pid); + sp_proc_source_populate_maps (self, pid); + } + + return; + } + + if (NULL == (dir = g_dir_open ("/proc", 0, NULL))) + return; + + while (NULL != (name = g_dir_read_name (dir))) + { + GPid pid; + char *end; + + pid = strtol (name, &end, 10); + if (pid <= 0 || *end != '\0') + continue; + + sp_proc_source_populate_process (self, pid); + sp_proc_source_populate_maps (self, pid); + } + + g_dir_close (dir); +} + +static void +sp_proc_source_start (SpSource *source) +{ + SpProcSource *self = (SpProcSource *)source; + + g_assert (SP_IS_PROC_SOURCE (self)); + g_assert (self->writer != NULL); + + sp_proc_source_populate (self); + sp_source_emit_finished (source); +} + +static void +sp_proc_source_stop (SpSource *source) +{ + SpProcSource *self = (SpProcSource *)source; + + g_assert (SP_IS_PROC_SOURCE (self)); + + g_clear_pointer (&self->writer, sp_capture_writer_unref); +} + +static void +sp_proc_source_set_writer (SpSource *source, + SpCaptureWriter *writer) +{ + SpProcSource *self = (SpProcSource *)source; + + g_assert (SP_IS_PROC_SOURCE (self)); + g_assert (writer != NULL); + + self->writer = sp_capture_writer_ref (writer); +} + +static void +sp_proc_source_add_pid (SpSource *source, + GPid pid) +{ + SpProcSource *self = (SpProcSource *)source; + guint i; + + g_assert (SP_IS_PROC_SOURCE (self)); + g_assert (pid > -1); + + for (i = 0; i < self->pids->len; i++) + { + GPid ele = g_array_index (self->pids, GPid, i); + + if (ele == pid) + return; + } + + g_array_append_val (self->pids, pid); +} + +static void +source_iface_init (SpSourceInterface *iface) +{ + iface->set_writer = sp_proc_source_set_writer; + iface->start = sp_proc_source_start; + iface->stop = sp_proc_source_stop; + iface->add_pid = sp_proc_source_add_pid; +} + +static void +sp_proc_source_finalize (GObject *object) +{ + SpProcSource *self = (SpProcSource *)object; + + g_clear_pointer (&self->writer, sp_capture_writer_unref); + g_clear_pointer (&self->pids, g_array_unref); + + G_OBJECT_CLASS (sp_proc_source_parent_class)->finalize (object); +} + +static void +sp_proc_source_class_init (SpProcSourceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_proc_source_finalize; +} + +static void +sp_proc_source_init (SpProcSource *self) +{ + self->pids = g_array_new (FALSE, FALSE, sizeof (GPid)); +} + +SpSource * +sp_proc_source_new (void) +{ + return g_object_new (SP_TYPE_PROC_SOURCE, NULL); +} diff --git a/lib/sp-proc-source.h b/lib/sp-proc-source.h new file mode 100644 index 00000000..b0883edf --- /dev/null +++ b/lib/sp-proc-source.h @@ -0,0 +1,36 @@ +/* sp-proc-source.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PROC_SOURCE_H +#define SP_PROC_SOURCE_H + +#include "sp-source.h" + +G_BEGIN_DECLS + +#define SP_TYPE_PROC_SOURCE (sp_proc_source_get_type()) + +G_DECLARE_FINAL_TYPE (SpProcSource, sp_proc_source, SP, PROC_SOURCE, GObject) + +SpSource *sp_proc_source_new (void); +gchar *sp_proc_source_get_command_line (GPid pid, + gboolean *is_kernel); + +G_END_DECLS + +#endif /* SP_PROC_SOURCE_H */ diff --git a/lib/sp-process-model-item.c b/lib/sp-process-model-item.c new file mode 100644 index 00000000..7067dc61 --- /dev/null +++ b/lib/sp-process-model-item.c @@ -0,0 +1,191 @@ +/* sp-process-model-item.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-process-model-item.h" +#include "sp-proc-source.h" + +struct _SpProcessModelItem +{ + GObject parent_instance; + GPid pid; + gchar *command_line; + guint is_kernel : 1; +}; + +G_DEFINE_TYPE (SpProcessModelItem, sp_process_model_item, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_COMMAND_LINE, + PROP_PID, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +sp_process_model_item_finalize (GObject *object) +{ + SpProcessModelItem *self = (SpProcessModelItem *)object; + + g_clear_pointer (&self->command_line, g_free); + + G_OBJECT_CLASS (sp_process_model_item_parent_class)->finalize (object); +} + +static void +sp_process_model_item_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpProcessModelItem *self = SP_PROCESS_MODEL_ITEM (object); + + switch (prop_id) + { + case PROP_COMMAND_LINE: + g_value_set_string (value, self->command_line); + break; + + case PROP_PID: + g_value_set_int (value, self->pid); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_process_model_item_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpProcessModelItem *self = SP_PROCESS_MODEL_ITEM (object); + + switch (prop_id) + { + case PROP_COMMAND_LINE: + self->command_line = g_value_dup_string (value); + break; + + case PROP_PID: + self->pid = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_process_model_item_class_init (SpProcessModelItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_process_model_item_finalize; + object_class->get_property = sp_process_model_item_get_property; + object_class->set_property = sp_process_model_item_set_property; + + properties [PROP_COMMAND_LINE] = + g_param_spec_string ("command-line", + "Command Line", + "Command Line", + NULL, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_PID] = + g_param_spec_int ("pid", + "Pid", + "Pid", + -1, + G_MAXINT, + -1, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sp_process_model_item_init (SpProcessModelItem *self) +{ +} + +SpProcessModelItem * +sp_process_model_item_new (GPid pid) +{ + SpProcessModelItem *ret; + gchar *cmdline; + gboolean is_kernel; + + cmdline = sp_proc_source_get_command_line (pid, &is_kernel); + + ret = g_object_new (SP_TYPE_PROCESS_MODEL_ITEM, + "command-line", cmdline, + "pid", (int)pid, + NULL); + ret->is_kernel = is_kernel; + + g_free (cmdline); + + return ret; +} + +guint +sp_process_model_item_hash (SpProcessModelItem *self) +{ + g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), 0); + + return self->pid; +} + +gboolean +sp_process_model_item_equal (SpProcessModelItem *self, + SpProcessModelItem *other) +{ + g_assert (SP_IS_PROCESS_MODEL_ITEM (self)); + g_assert (SP_IS_PROCESS_MODEL_ITEM (other)); + + return ((self->pid == other->pid) && + (g_strcmp0 (self->command_line, other->command_line) == 0)); +} + +GPid +sp_process_model_item_get_pid (SpProcessModelItem *self) +{ + g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), 0); + + return self->pid; +} + +const gchar * +sp_process_model_item_get_command_line (SpProcessModelItem *self) +{ + g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), NULL); + + return self->command_line; +} + +gboolean +sp_process_model_item_is_kernel (SpProcessModelItem *self) +{ + g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), FALSE); + + return self->is_kernel; +} diff --git a/lib/sp-process-model-item.h b/lib/sp-process-model-item.h new file mode 100644 index 00000000..850a741a --- /dev/null +++ b/lib/sp-process-model-item.h @@ -0,0 +1,40 @@ +/* sp-process-model-item.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PROCESS_MODEL_ITEM_H +#define SP_PROCESS_MODEL_ITEM_H + +#include + +G_BEGIN_DECLS + +#define SP_TYPE_PROCESS_MODEL_ITEM (sp_process_model_item_get_type()) + +G_DECLARE_FINAL_TYPE (SpProcessModelItem, sp_process_model_item, SP, PROCESS_MODEL_ITEM, GObject) + +SpProcessModelItem *sp_process_model_item_new (GPid pid); +guint sp_process_model_item_hash (SpProcessModelItem *self); +gboolean sp_process_model_item_equal (SpProcessModelItem *self, + SpProcessModelItem *other); +GPid sp_process_model_item_get_pid (SpProcessModelItem *self); +const gchar *sp_process_model_item_get_command_line (SpProcessModelItem *self); +gboolean sp_process_model_item_is_kernel (SpProcessModelItem *self); + +G_END_DECLS + +#endif /* SP_PROCESS_MODEL_ITEM_H */ diff --git a/lib/sp-process-model-row.c b/lib/sp-process-model-row.c new file mode 100644 index 00000000..b8e6e77b --- /dev/null +++ b/lib/sp-process-model-row.c @@ -0,0 +1,209 @@ +/* sp-process-model-row.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-process-model-row.h" + +typedef struct +{ + SpProcessModelItem *item; + + GtkLabel *label; + GtkLabel *pid; + GtkImage *image; + GtkImage *check; +} SpProcessModelRowPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SpProcessModelRow, sp_process_model_row, GTK_TYPE_LIST_BOX_ROW) + +enum { + PROP_0, + PROP_ITEM, + PROP_SELECTED, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +GtkWidget * +sp_process_model_row_new (SpProcessModelItem *item) +{ + return g_object_new (SP_TYPE_PROCESS_MODEL_ROW, + "item", item, + NULL); +} + +SpProcessModelItem * +sp_process_model_row_get_item (SpProcessModelRow *self) +{ + SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROCESS_MODEL_ROW (self), NULL); + + return priv->item; +} + +static void +sp_process_model_row_set_item (SpProcessModelRow *self, + SpProcessModelItem *item) +{ + SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self); + + g_assert (SP_IS_PROCESS_MODEL_ROW (self)); + g_assert (SP_IS_PROCESS_MODEL_ITEM (item)); + + if (g_set_object (&priv->item, item)) + { + const gchar *command_line; + g_auto(GStrv) parts = NULL; + g_autofree gchar *pidstr = NULL; + GPid pid; + + command_line = sp_process_model_item_get_command_line (item); + parts = g_strsplit (command_line ?: "", "\n", 0); + gtk_label_set_label (priv->label, parts [0]); + + pid = sp_process_model_item_get_pid (item); + pidstr = g_strdup_printf ("%u", pid); + gtk_label_set_label (priv->pid, pidstr); + gtk_label_set_use_markup (priv->pid, TRUE); + } +} + +gboolean +sp_process_model_row_get_selected (SpProcessModelRow *self) +{ + SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROCESS_MODEL_ROW (self), FALSE); + + return gtk_widget_get_visible (GTK_WIDGET (priv->check)); +} + +void +sp_process_model_row_set_selected (SpProcessModelRow *self, + gboolean selected) +{ + SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self); + + g_return_if_fail (SP_IS_PROCESS_MODEL_ROW (self)); + + selected = !!selected; + + if (selected != sp_process_model_row_get_selected (self)) + { + gtk_widget_set_visible (GTK_WIDGET (priv->check), selected); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTED]); + } +} + +static void +sp_process_model_row_finalize (GObject *object) +{ + SpProcessModelRow *self = (SpProcessModelRow *)object; + SpProcessModelRowPrivate *priv = sp_process_model_row_get_instance_private (self); + + g_clear_object (&priv->item); + + G_OBJECT_CLASS (sp_process_model_row_parent_class)->finalize (object); +} + +static void +sp_process_model_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpProcessModelRow *self = SP_PROCESS_MODEL_ROW (object); + + switch (prop_id) + { + case PROP_ITEM: + g_value_set_object (value, sp_process_model_row_get_item (self)); + break; + + case PROP_SELECTED: + g_value_set_boolean (value, sp_process_model_row_get_selected (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_process_model_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpProcessModelRow *self = SP_PROCESS_MODEL_ROW (object); + + switch (prop_id) + { + case PROP_ITEM: + sp_process_model_row_set_item (self, g_value_get_object (value)); + break; + + case PROP_SELECTED: + sp_process_model_row_set_selected (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_process_model_row_class_init (SpProcessModelRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = sp_process_model_row_finalize; + object_class->get_property = sp_process_model_row_get_property; + object_class->set_property = sp_process_model_row_set_property; + + properties [PROP_ITEM] = + g_param_spec_object ("item", + "Item", + "Item", + SP_TYPE_PROCESS_MODEL_ITEM, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SELECTED] = + g_param_spec_boolean ("selected", + "Selected", + "Selected", + FALSE, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/sysprof/ui/sp-process-model-row.ui"); + gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, image); + gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, label); + gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, pid); + gtk_widget_class_bind_template_child_private (widget_class, SpProcessModelRow, check); +} + +static void +sp_process_model_row_init (SpProcessModelRow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/lib/sp-process-model-row.h b/lib/sp-process-model-row.h new file mode 100644 index 00000000..2d9f1df3 --- /dev/null +++ b/lib/sp-process-model-row.h @@ -0,0 +1,46 @@ +/* sp-process-model-row.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PROCESS_MODEL_ROW_H +#define SP_PROCESS_MODEL_ROW_H + +#include + +#include "sp-process-model-item.h" + +G_BEGIN_DECLS + +#define SP_TYPE_PROCESS_MODEL_ROW (sp_process_model_row_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpProcessModelRow, sp_process_model_row, SP, PROCESS_MODEL_ROW, GtkListBoxRow) + +struct _SpProcessModelRowClass +{ + GtkListBoxRowClass parent; +}; + +GtkWidget *sp_process_model_row_new (SpProcessModelItem *item); +SpProcessModelItem *sp_process_model_row_get_item (SpProcessModelRow *self); +gboolean sp_process_model_row_get_selected (SpProcessModelRow *self); +void sp_process_model_row_set_selected (SpProcessModelRow *self, + gboolean selected); + +G_END_DECLS + +#endif /* SP_PROCESS_MODEL_ROW_H */ + diff --git a/lib/sp-process-model.c b/lib/sp-process-model.c new file mode 100644 index 00000000..eba686e0 --- /dev/null +++ b/lib/sp-process-model.c @@ -0,0 +1,293 @@ +/* sp-process-model.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-process-model.h" +#include "sp-process-model-item.h" + +#define QUEUE_RELOAD_TIMEOUT_MSEC 100 + +struct _SpProcessModel +{ + GObject parent_instance; + guint reload_source; + GPtrArray *items; +}; + +static void list_model_iface_init (GListModelInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpProcessModel, sp_process_model, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static void +sp_process_model_finalize (GObject *object) +{ + SpProcessModel *self = (SpProcessModel *)object; + + if (self->reload_source) + { + g_source_remove (self->reload_source); + self->reload_source = 0; + } + + g_clear_pointer (&self->items, g_ptr_array_unref); + + G_OBJECT_CLASS (sp_process_model_parent_class)->finalize (object); +} + +static void +sp_process_model_class_init (SpProcessModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_process_model_finalize; +} + +static void +sp_process_model_init (SpProcessModel *self) +{ + self->items = g_ptr_array_new_with_free_func (g_object_unref); + + sp_process_model_queue_reload (self); +} + +static guint +find_index (GPtrArray *ar, + GPid pid) +{ + guint i; + + g_assert (ar != NULL); + + for (i = 0; i < ar->len; i++) + { + SpProcessModelItem *item = g_ptr_array_index (ar, i); + GPid item_pid = sp_process_model_item_get_pid (item); + + g_assert (pid != item_pid); + + if (item_pid > pid) + return i; + } + + return ar->len; +} + +static void +sp_process_model_merge_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SpProcessModel *self = (SpProcessModel *)object; + g_autoptr(GPtrArray) ret = NULL; + g_autoptr(GHashTable) old_hash = NULL; + g_autoptr(GHashTable) new_hash = NULL; + GError *error = NULL; + guint i; + + g_assert (SP_IS_PROCESS_MODEL (self)); + g_assert (G_IS_TASK (result)); + + ret = g_task_propagate_pointer (G_TASK (result), &error); + + if (ret == NULL) + { + g_warning ("%s", error->message); + g_clear_error (&error); + return; + } + + /* + * TODO: Clearly this could be optimized to walk both arrays at once + * and do a proper 2-way merge. + */ + + old_hash = g_hash_table_new ((GHashFunc)sp_process_model_item_hash, + (GEqualFunc)sp_process_model_item_equal); + new_hash = g_hash_table_new ((GHashFunc)sp_process_model_item_hash, + (GEqualFunc)sp_process_model_item_equal); + + for (i = 0; i < self->items->len; i++) + { + SpProcessModelItem *item = g_ptr_array_index (self->items, i); + + g_hash_table_insert (old_hash, item, NULL); + } + + for (i = 0; i < ret->len; i++) + { + SpProcessModelItem *item = g_ptr_array_index (ret, i); + + g_hash_table_insert (new_hash, item, NULL); + } + + for (i = self->items->len; i > 0; i--) + { + guint index = i - 1; + SpProcessModelItem *item = g_ptr_array_index (self->items, index); + + if (!g_hash_table_contains (new_hash, item)) + { + g_ptr_array_remove_index (self->items, index); + g_list_model_items_changed (G_LIST_MODEL (self), index, 1, 0); + } + } + + for (i = 0; i < ret->len; i++) + { + SpProcessModelItem *item = g_ptr_array_index (ret, i); + GPid pid; + guint index; + + if (g_hash_table_contains (old_hash, item)) + continue; + + pid = sp_process_model_item_get_pid (item); + index = find_index (self->items, pid); + + g_ptr_array_insert (self->items, index, g_object_ref (item)); + g_list_model_items_changed (G_LIST_MODEL (self), index, 0, 1); + } +} + +static gint +compare_by_pid (gconstpointer a, + gconstpointer b) +{ + SpProcessModelItem **aitem = (SpProcessModelItem **)a; + SpProcessModelItem **bitem = (SpProcessModelItem **)b; + + return sp_process_model_item_get_pid (*aitem) - sp_process_model_item_get_pid (*bitem); +} + +static void +sp_process_model_reload_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autoptr(GPtrArray) ret = NULL; + const gchar *name; + GError *error = NULL; + GDir *dir; + + g_assert (SP_IS_PROCESS_MODEL (source_object)); + g_assert (G_IS_TASK (task)); + + dir = g_dir_open ("/proc", 0, &error); + + if (dir == NULL) + { + g_task_return_error (task, error); + return; + } + + ret = g_ptr_array_new_with_free_func (g_object_unref); + + while ((name = g_dir_read_name (dir))) + { + SpProcessModelItem *item; + GPid pid; + gchar *end; + + pid = strtol (name, &end, 10); + if (pid <= 0 || *end != '\0') + continue; + + item = sp_process_model_item_new (pid); + + if (sp_process_model_item_is_kernel (item)) + { + g_object_unref (item); + continue; + } + + g_ptr_array_add (ret, item); + } + + g_dir_close (dir); + + g_ptr_array_sort (ret, compare_by_pid); + g_task_return_pointer (task, g_ptr_array_ref (ret), (GDestroyNotify)g_ptr_array_unref); +} + +static gboolean +sp_process_model_do_reload (gpointer user_data) +{ + SpProcessModel *self = user_data; + g_autoptr(GTask) task = NULL; + + self->reload_source = 0; + + task = g_task_new (self, NULL, sp_process_model_merge_cb, NULL); + g_task_run_in_thread (task, sp_process_model_reload_worker); + + return G_SOURCE_REMOVE; +} + +SpProcessModel * +sp_process_model_new (void) +{ + return g_object_new (SP_TYPE_PROCESS_MODEL, NULL); +} + +void +sp_process_model_queue_reload (SpProcessModel *self) +{ + g_return_if_fail (SP_IS_PROCESS_MODEL (self)); + + if (self->reload_source == 0) + self->reload_source = g_timeout_add (QUEUE_RELOAD_TIMEOUT_MSEC, + sp_process_model_do_reload, + self); +} + +static GType +sp_process_model_get_item_type (GListModel *model) +{ + return SP_TYPE_PROCESS_MODEL_ITEM; +} + +static guint +sp_process_model_get_n_items (GListModel *model) +{ + SpProcessModel *self = (SpProcessModel *)model; + + return self->items->len; +} + +static gpointer +sp_process_model_get_item (GListModel *model, + guint position) +{ + SpProcessModel *self = (SpProcessModel *)model; + + g_return_val_if_fail (SP_IS_PROCESS_MODEL (self), NULL); + g_return_val_if_fail (position < self->items->len, NULL); + + return g_object_ref (g_ptr_array_index (self->items, position)); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sp_process_model_get_item_type; + iface->get_n_items = sp_process_model_get_n_items; + iface->get_item = sp_process_model_get_item; +} diff --git a/lib/sp-process-model.h b/lib/sp-process-model.h new file mode 100644 index 00000000..8b16037b --- /dev/null +++ b/lib/sp-process-model.h @@ -0,0 +1,35 @@ +/* sp-process-model.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PROCESS_MODEL_H +#define SP_PROCESS_MODEL_H + +#include + +G_BEGIN_DECLS + +#define SP_TYPE_PROCESS_MODEL (sp_process_model_get_type()) + +G_DECLARE_FINAL_TYPE (SpProcessModel, sp_process_model, SP, PROCESS_MODEL, GObject) + +SpProcessModel *sp_process_model_new (void); +void sp_process_model_queue_reload (SpProcessModel *self); + +G_END_DECLS + +#endif /* SP_PROCESS_MODEL_H */ diff --git a/lib/sp-profile.c b/lib/sp-profile.c new file mode 100644 index 00000000..9a4d701e --- /dev/null +++ b/lib/sp-profile.c @@ -0,0 +1,81 @@ +/* sp-profile.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-profile.h" + +G_DEFINE_INTERFACE (SpProfile, sp_profile, G_TYPE_OBJECT) + +static void +dummy_generate (SpProfile *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + task = g_task_new (self, cancellable, callback, user_data); + g_task_return_boolean (task, TRUE); +} + +static gboolean +dummy_generate_finish (SpProfile *self, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sp_profile_default_init (SpProfileInterface *iface) +{ + iface->generate = dummy_generate; + iface->generate_finish = dummy_generate_finish; +} + +void +sp_profile_generate (SpProfile *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (SP_IS_PROFILE (self)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + SP_PROFILE_GET_IFACE (self)->generate (self, cancellable, callback, user_data); +} + +gboolean +sp_profile_generate_finish (SpProfile *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SP_IS_PROFILE (self), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + + return SP_PROFILE_GET_IFACE (self)->generate_finish (self, result, error); +} + +void +sp_profile_set_reader (SpProfile *self, + SpCaptureReader *reader) +{ + g_return_if_fail (SP_IS_PROFILE (self)); + g_return_if_fail (reader != NULL); + + SP_PROFILE_GET_IFACE (self)->set_reader (self, reader); +} diff --git a/lib/sp-profile.h b/lib/sp-profile.h new file mode 100644 index 00000000..60942dea --- /dev/null +++ b/lib/sp-profile.h @@ -0,0 +1,59 @@ +/* sp-profile.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PROFILE_H +#define SP_PROFILE_H + +#include + +#include "sp-capture-reader.h" + +G_BEGIN_DECLS + +#define SP_TYPE_PROFILE (sp_profile_get_type ()) + +G_DECLARE_INTERFACE (SpProfile, sp_profile, SP, PROFILE, GObject) + +struct _SpProfileInterface +{ + GTypeInterface parent; + + void (*set_reader) (SpProfile *self, + SpCaptureReader *reader); + void (*generate) (SpProfile *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*generate_finish) (SpProfile *self, + GAsyncResult *result, + GError **error); +}; + +void sp_profile_set_reader (SpProfile *self, + SpCaptureReader *reader); +void sp_profile_generate (SpProfile *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean sp_profile_generate_finish (SpProfile *self, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* SP_PROFILE_H */ diff --git a/lib/sp-profiler-menu-button.c b/lib/sp-profiler-menu-button.c new file mode 100644 index 00000000..6d78aaa5 --- /dev/null +++ b/lib/sp-profiler-menu-button.c @@ -0,0 +1,857 @@ +/* sp-profiler-menu-button.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 + +#include "sp-model-filter.h" +#include "sp-process-model.h" +#include "sp-process-model-item.h" +#include "sp-process-model-row.h" +#include "sp-profiler-menu-button.h" + +typedef struct +{ + SpProfiler *profiler; + SpModelFilter *process_filter; + + /* Gtk template widgets */ + GtkLabel *label; + GtkPopover *popover; + GtkEntry *process_filter_entry; + GtkListBox *process_list_box; + SpProcessModel *process_model; + GtkBox *processes_box; + GtkEntry *spawn_entry; + GtkStack *stack; + GtkSwitch *whole_system_switch; + GtkTreeView *env_tree_view; + GtkTreeViewColumn *env_key_column; + GtkTreeViewColumn *env_value_column; + GtkCellRenderer *key_cell; + GtkCellRenderer *value_cell; + GtkCheckButton *inherit_environ; + + /* Property Bindings */ + GBinding *inherit_binding; + GBinding *list_sensitive_binding; + GBinding *mutable_binding; + GBinding *whole_system_binding; + + /* Signal handlers */ + gulong notify_whole_system_handler; + + /* GSources */ + guint save_env_source; +} SpProfilerMenuButtonPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SpProfilerMenuButton, sp_profiler_menu_button, GTK_TYPE_MENU_BUTTON) + +enum { + PROP_0, + PROP_PROFILER, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +GtkWidget * +sp_profiler_menu_button_new (void) +{ + return g_object_new (SP_TYPE_PROFILER_MENU_BUTTON, NULL); +} + +static void +sp_profiler_menu_button_update_label (SpProfilerMenuButton *self) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + g_autofree gchar *str = NULL; + const gchar *visible_child; + const GPid *pids; + guint n_pids = 0; + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + + if (priv->profiler == NULL) + { + gtk_label_set_label (priv->label, ""); + return; + } + + visible_child = gtk_stack_get_visible_child_name (priv->stack); + + if (g_strcmp0 (visible_child, "spawn") == 0) + { + const gchar *text; + + text = gtk_entry_get_text (priv->spawn_entry); + + if (text && *text) + gtk_label_set_label (priv->label, text); + else if (sp_profiler_get_whole_system (priv->profiler)) + gtk_label_set_label (priv->label, _("All Processes")); + else + gtk_label_set_label (priv->label, _("New Process")); + + sp_profiler_set_spawn (priv->profiler, text && *text); + + return; + } + + sp_profiler_set_spawn (priv->profiler, FALSE); + + pids = sp_profiler_get_pids (priv->profiler, &n_pids); + + if (n_pids == 0 || sp_profiler_get_whole_system (priv->profiler)) + { + gtk_label_set_label (priv->label, _("All Processes")); + return; + } + + if (n_pids == 1) + { + str = g_strdup_printf (_("Process %d"), pids[0]); + gtk_label_set_label (priv->label, str); + return; + } + + str = g_strdup_printf (_("%u Processes"), n_pids); + gtk_label_set_label (priv->label, str); +} + +static void +clear_selected_flags (GtkWidget *widget, + gpointer user_data) +{ + sp_process_model_row_set_selected (SP_PROCESS_MODEL_ROW (widget), FALSE); +} + +static void +add_binding (GBinding **binding, + gpointer src, + const gchar *src_property, + gpointer dst, + const gchar *dst_property, + GBindingFlags flags) +{ + g_assert (binding != NULL); + g_assert (*binding == NULL); + g_assert (src != NULL); + g_assert (src_property != NULL); + g_assert (dst != NULL); + g_assert (dst_property != NULL); + + *binding = g_object_bind_property (src, src_property, + dst, dst_property, + flags); + g_object_add_weak_pointer (G_OBJECT (*binding), (gpointer *)binding); +} + +static void +clear_binding (GBinding **binding) +{ + g_assert (binding != NULL); + g_assert (!*binding || G_IS_BINDING (*binding)); + + if (*binding != NULL) + { + g_object_remove_weak_pointer (G_OBJECT (*binding), (gpointer *)binding); + g_binding_unbind (*binding); + *binding = NULL; + } +} + +static void +sp_profiler_menu_button_connect (SpProfilerMenuButton *self) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (SP_IS_PROFILER (priv->profiler)); + + add_binding (&priv->mutable_binding, + priv->profiler, "is-mutable", + self, "sensitive", + G_BINDING_SYNC_CREATE); + + add_binding (&priv->whole_system_binding, + priv->profiler, "whole-system", + priv->whole_system_switch, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + add_binding (&priv->list_sensitive_binding, + priv->profiler, "whole-system", + priv->processes_box, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + add_binding (&priv->inherit_binding, + priv->inherit_environ, "active", + priv->profiler, "spawn-inherit-environ", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + + priv->notify_whole_system_handler = + g_signal_connect_object (priv->profiler, + "notify::whole-system", + G_CALLBACK (sp_profiler_menu_button_update_label), + self, + G_CONNECT_SWAPPED); + + sp_profiler_menu_button_update_label (self); +} + +static void +sp_profiler_menu_button_disconnect (SpProfilerMenuButton *self) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (SP_IS_PROFILER (priv->profiler)); + + clear_binding (&priv->mutable_binding); + clear_binding (&priv->whole_system_binding); + clear_binding (&priv->list_sensitive_binding); + clear_binding (&priv->inherit_binding); + + g_signal_handler_disconnect (priv->profiler, priv->notify_whole_system_handler); + priv->notify_whole_system_handler = 0; + + gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); + + g_clear_object (&priv->profiler); + + gtk_container_foreach (GTK_CONTAINER (priv->process_list_box), + clear_selected_flags, + NULL); + + sp_profiler_menu_button_update_label (self); +} + +/** + * sp_profiler_menu_button_get_profiler: + * @self: An #SpProfilerMenuButton + * + * Gets the profiler instance that is being configured. + * + * Returns: (nullable) (transfer none): An #SpProfiler or %NULL. + */ +SpProfiler * +sp_profiler_menu_button_get_profiler (SpProfilerMenuButton *self) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER_MENU_BUTTON (self), NULL); + + return priv->profiler; +} + +void +sp_profiler_menu_button_set_profiler (SpProfilerMenuButton *self, + SpProfiler *profiler) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER_MENU_BUTTON (self)); + g_return_if_fail (!profiler || SP_IS_PROFILER (profiler)); + + if (priv->profiler != profiler) + { + if (priv->profiler != NULL) + sp_profiler_menu_button_disconnect (self); + + if (profiler != NULL) + { + priv->profiler = g_object_ref (profiler); + sp_profiler_menu_button_connect (self); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROFILER]); + } +} + +static void +sp_profiler_menu_button_row_activated (SpProfilerMenuButton *self, + SpProcessModelRow *row, + GtkListBox *list_box) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + gboolean selected; + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (SP_IS_PROCESS_MODEL_ROW (row)); + g_assert (GTK_IS_LIST_BOX (list_box)); + + selected = !sp_process_model_row_get_selected (row); + sp_process_model_row_set_selected (row, selected); + + if (priv->profiler != NULL) + { + SpProcessModelItem *item = sp_process_model_row_get_item (row); + GPid pid = sp_process_model_item_get_pid (item); + + if (selected) + sp_profiler_add_pid (priv->profiler, pid); + else + sp_profiler_remove_pid (priv->profiler, pid); + } + + sp_profiler_menu_button_update_label (self); +} + +static GtkWidget * +sp_profiler_menu_button_create_row (gpointer itemptr, + gpointer user_data) +{ + SpProcessModelItem *item = itemptr; + SpProfilerMenuButton *self = user_data; + + g_assert (SP_IS_PROCESS_MODEL_ITEM (item)); + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + + return g_object_new (SP_TYPE_PROCESS_MODEL_ROW, + "item", item, + "visible", TRUE, + NULL); +} + +static void +sp_profiler_menu_button_clicked (GtkButton *button) +{ + SpProfilerMenuButton *self = (SpProfilerMenuButton *)button; + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + + sp_process_model_queue_reload (priv->process_model); + + GTK_BUTTON_CLASS (sp_profiler_menu_button_parent_class)->clicked (button); +} + +static void +sp_profiler_menu_button_filter_changed (SpProfilerMenuButton *self, + GtkEntry *entry) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (GTK_IS_ENTRY (entry)); + + sp_model_filter_invalidate (priv->process_filter); +} + +static gboolean +sp_profiler_menu_button_filter_func (GObject *object, + gpointer user_data) +{ + SpProfilerMenuButton *self = user_data; + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + const gchar *cmdline; + const gchar *text; + gboolean ret; + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (SP_IS_PROCESS_MODEL_ITEM (object)); + + text = gtk_entry_get_text (priv->process_filter_entry); + if (!text || !*text) + return TRUE; + + cmdline = sp_process_model_item_get_command_line (SP_PROCESS_MODEL_ITEM (object)); + if (!cmdline) + return FALSE; + + ret = (strstr (cmdline, text) != NULL); + + return ret; +} + +static void +sp_profiler_menu_button_constructed (GObject *object) +{ + SpProfilerMenuButton *self = (SpProfilerMenuButton *)object; + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + + priv->process_filter = sp_model_filter_new (G_LIST_MODEL (priv->process_model)); + sp_model_filter_set_filter_func (priv->process_filter, + sp_profiler_menu_button_filter_func, + self, NULL); + gtk_list_box_bind_model (priv->process_list_box, + G_LIST_MODEL (priv->process_filter), + sp_profiler_menu_button_create_row, + self, NULL); + + G_OBJECT_CLASS (sp_profiler_menu_button_parent_class)->constructed (object); +} + +static void +sp_profiler_menu_button_realize (GtkWidget *widget) +{ + SpProfilerMenuButton *self = (SpProfilerMenuButton *)widget; + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + g_autoptr(GSettings) settings = NULL; + g_auto(GStrv) env = NULL; + + GTK_WIDGET_CLASS (sp_profiler_menu_button_parent_class)->realize (widget); + + settings = g_settings_new ("org.gnome.sysprof2"); + + env = g_settings_get_strv (settings, "last-spawn-env"); + + g_settings_bind (settings, "last-spawn-argv", + priv->spawn_entry, "text", + G_SETTINGS_BIND_DEFAULT); + g_settings_bind (settings, "last-spawn-inherit-env", + priv->inherit_environ, "active", + G_SETTINGS_BIND_DEFAULT); + + if (env) + { + GtkTreeModel *model; + GtkTreeIter iter; + guint i; + + model = gtk_tree_view_get_model (priv->env_tree_view); + gtk_list_store_clear (GTK_LIST_STORE (model)); + + for (i = 0; env [i]; i++) + { + const gchar *key = env [i]; + const gchar *value = NULL; + gchar *eq = strchr (env[i], '='); + + if (eq) + { + *eq = '\0'; + value = eq + 1; + } + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + 0, key, + 1, value, + -1); + } + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + } +} + +static gboolean +save_environ_to_gsettings (gpointer data) +{ + SpProfilerMenuButton *self = data; + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + g_autoptr(GPtrArray) ar = NULL; + g_autoptr(GSettings) settings = NULL; + GtkTreeModel *model; + GtkTreeIter iter; + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + + priv->save_env_source = 0; + + settings = g_settings_new ("org.gnome.sysprof2"); + + model = gtk_tree_view_get_model (priv->env_tree_view); + + ar = g_ptr_array_new_with_free_func (g_free); + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + g_autofree gchar *key = NULL; + g_autofree gchar *value = NULL; + + gtk_tree_model_get (model, &iter, + 0, &key, + 1, &value, + -1); + + if (!key || !*key) + continue; + + g_ptr_array_add (ar, g_strdup_printf ("%s=%s", key, value ? value : "")); + } + while (gtk_tree_model_iter_next (model, &iter)); + } + + g_ptr_array_add (ar, NULL); + + g_settings_set_strv (settings, "last-spawn-env", (const gchar * const *)ar->pdata); + + return G_SOURCE_REMOVE; +} + + +static void +sp_profiler_menu_button_destroy (GtkWidget *widget) +{ + SpProfilerMenuButton *self = (SpProfilerMenuButton *)widget; + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + if (priv->profiler != NULL) + { + sp_profiler_menu_button_disconnect (self); + g_clear_object (&priv->profiler); + } + + if (priv->save_env_source) + save_environ_to_gsettings (self); + + g_clear_object (&priv->process_filter); + + GTK_WIDGET_CLASS (sp_profiler_menu_button_parent_class)->destroy (widget); +} + +static void +sp_profiler_menu_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpProfilerMenuButton *self = SP_PROFILER_MENU_BUTTON (object); + + switch (prop_id) + { + case PROP_PROFILER: + g_value_set_object (value, sp_profiler_menu_button_get_profiler (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_profiler_menu_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpProfilerMenuButton *self = SP_PROFILER_MENU_BUTTON (object); + + switch (prop_id) + { + case PROP_PROFILER: + sp_profiler_menu_button_set_profiler (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_profiler_menu_button_class_init (SpProfilerMenuButtonClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass); + + object_class->constructed = sp_profiler_menu_button_constructed; + object_class->get_property = sp_profiler_menu_button_get_property; + object_class->set_property = sp_profiler_menu_button_set_property; + + widget_class->destroy = sp_profiler_menu_button_destroy; + widget_class->realize = sp_profiler_menu_button_realize; + + button_class->clicked = sp_profiler_menu_button_clicked; + + properties [PROP_PROFILER] = + g_param_spec_object ("profiler", + "Profiler", + "Profiler", + SP_TYPE_PROFILER, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sp-profiler-menu-button.ui"); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, env_key_column); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, env_tree_view); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, env_value_column); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, inherit_environ); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, key_cell); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, label); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, popover); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, process_filter_entry); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, process_list_box); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, process_model); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, processes_box); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, spawn_entry); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, stack); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, value_cell); + gtk_widget_class_bind_template_child_private (widget_class, SpProfilerMenuButton, whole_system_switch); +} + +static void +sp_profiler_menu_button_env_row_changed (SpProfilerMenuButton *self, + GtkTreePath *tree_path, + GtkTreeIter *iter, + gpointer user_data) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + g_autoptr(GPtrArray) env = NULL; + GtkTreeModel *model; + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (tree_path != NULL); + g_assert (iter != NULL); + + /* queue saving settings to gsettings */ + if (priv->save_env_source) + g_source_remove (priv->save_env_source); + priv->save_env_source = g_timeout_add_seconds (1, save_environ_to_gsettings, self); + + /* sync the environ to the profiler */ + env = g_ptr_array_new_with_free_func (g_free); + model = gtk_tree_view_get_model (priv->env_tree_view); + if (gtk_tree_model_get_iter_first (model, iter)) + { + do + { + g_autofree gchar *key = NULL; + g_autofree gchar *value = NULL; + + gtk_tree_model_get (model, iter, + 0, &key, + 1, &value, + -1); + + if (key && *key) + g_ptr_array_add (env, g_strdup_printf ("%s=%s", key, value)); + } + while (gtk_tree_model_iter_next (model, iter)); + } + g_ptr_array_add (env, NULL); + sp_profiler_set_spawn_env (priv->profiler, (const gchar * const *)env->pdata); +} + +static void +on_backspace (SpProfilerMenuButton *self, + GtkEntry *entry) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + if (g_object_get_data (G_OBJECT (entry), "CELL_WAS_EMPTY")) + { + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (priv->env_tree_view); + selection = gtk_tree_view_get_selection (priv->env_tree_view); + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + gtk_cell_renderer_stop_editing (priv->key_cell, TRUE); + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + } + } + else + g_object_set_data (G_OBJECT (entry), "CELL_WAS_EMPTY", + GINT_TO_POINTER (*gtk_entry_get_text (entry) == '\0')); +} + +static void +sp_profiler_menu_button_env_key_editing_started (SpProfilerMenuButton *self, + GtkCellEditable *editable, + const gchar *path, + GtkCellRenderer *cell) +{ + g_signal_connect_object (editable, + "backspace", + G_CALLBACK (on_backspace), + self, + G_CONNECT_AFTER | G_CONNECT_SWAPPED); +} + +static void +sp_profiler_menu_button_env_key_edited (SpProfilerMenuButton *self, + const gchar *path, + const gchar *new_text, + GtkCellRendererText *cell) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + GtkTreeModel *model; + GtkTreePath *tree_path; + GtkTreeIter iter; + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (path != NULL); + g_assert (new_text != NULL); + g_assert (GTK_IS_CELL_RENDERER_TEXT (cell)); + + model = gtk_tree_view_get_model (priv->env_tree_view); + + tree_path = gtk_tree_path_new_from_string (path); + + if (gtk_tree_model_get_iter (model, &iter, tree_path)) + { + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + 0, new_text, + -1); + + if (!gtk_tree_model_iter_next (model, &iter)) + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + + gtk_tree_view_set_cursor_on_cell (priv->env_tree_view, + tree_path, + priv->env_value_column, + priv->value_cell, + TRUE); + } + + gtk_tree_path_free (tree_path); +} + +static void +sp_profiler_menu_button_env_value_edited (SpProfilerMenuButton *self, + const gchar *path, + const gchar *new_text, + GtkCellRendererText *cell) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + GtkTreeModel *model; + GtkTreePath *tree_path; + GtkTreeIter iter; + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (path != NULL); + g_assert (new_text != NULL); + g_assert (GTK_IS_CELL_RENDERER_TEXT (cell)); + + model = gtk_tree_view_get_model (priv->env_tree_view); + + tree_path = gtk_tree_path_new_from_string (path); + + if (gtk_tree_model_get_iter (model, &iter, tree_path)) + { + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + 1, new_text, + -1); + + if (!gtk_tree_model_iter_next (model, &iter)) + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + + gtk_tree_path_next (tree_path); + + gtk_tree_view_set_cursor_on_cell (priv->env_tree_view, + tree_path, + priv->env_key_column, + priv->key_cell, + TRUE); + } + + gtk_tree_path_free (tree_path); +} + +static void +sp_profiler_menu_button_validate_spawn (SpProfilerMenuButton *self, + GtkEntry *entry) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + g_auto(GStrv) argv = NULL; + const gchar *icon_name = NULL; + const gchar *text; + gint argc; + + g_assert (SP_IS_PROFILER_MENU_BUTTON (self)); + g_assert (GTK_IS_ENTRY (entry)); + + text = gtk_entry_get_text (entry); + + if (!g_shell_parse_argv (text, &argc, &argv, NULL)) + { + icon_name = "dialog-warning-symbolic"; + sp_profiler_set_spawn_argv (priv->profiler, NULL); + } + else + sp_profiler_set_spawn_argv (priv->profiler, (const gchar * const *)argv); + + gtk_entry_set_icon_from_icon_name (entry, + GTK_ENTRY_ICON_SECONDARY, + icon_name); +} + +static void +sp_profiler_menu_button_init (SpProfilerMenuButton *self) +{ + SpProfilerMenuButtonPrivate *priv = sp_profiler_menu_button_get_instance_private (self); + + gtk_widget_init_template (GTK_WIDGET (self)); + + g_signal_connect_object (priv->process_filter_entry, + "changed", + G_CALLBACK (sp_profiler_menu_button_filter_changed), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->spawn_entry, + "changed", + G_CALLBACK (sp_profiler_menu_button_update_label), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->spawn_entry, + "changed", + G_CALLBACK (sp_profiler_menu_button_validate_spawn), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->stack, + "notify::visible-child", + G_CALLBACK (sp_profiler_menu_button_update_label), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->process_list_box, + "row-activated", + G_CALLBACK (sp_profiler_menu_button_row_activated), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->key_cell, + "edited", + G_CALLBACK (sp_profiler_menu_button_env_key_edited), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->value_cell, + "edited", + G_CALLBACK (sp_profiler_menu_button_env_value_edited), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (gtk_tree_view_get_model (priv->env_tree_view), + "row-changed", + G_CALLBACK (sp_profiler_menu_button_env_row_changed), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->key_cell, + "editing-started", + G_CALLBACK (sp_profiler_menu_button_env_key_editing_started), + self, + G_CONNECT_SWAPPED); + + gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); +} diff --git a/lib/sp-profiler-menu-button.h b/lib/sp-profiler-menu-button.h new file mode 100644 index 00000000..fcce9d9a --- /dev/null +++ b/lib/sp-profiler-menu-button.h @@ -0,0 +1,44 @@ +/* sp-profiler-menu-button.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PROFILER_MENU_BUTTON_H +#define SP_PROFILER_MENU_BUTTON_H + +#include + +#include "sp-profiler.h" + +G_BEGIN_DECLS + +#define SP_TYPE_PROFILER_MENU_BUTTON (sp_profiler_menu_button_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpProfilerMenuButton, sp_profiler_menu_button, SP, PROFILER_MENU_BUTTON, GtkMenuButton) + +struct _SpProfilerMenuButtonClass +{ + GtkMenuButtonClass parent_class; +}; + +GtkWidget *sp_profiler_menu_button_new (void); +void sp_profiler_menu_button_set_profiler (SpProfilerMenuButton *self, + SpProfiler *profiler); +SpProfiler *sp_profiler_menu_button_get_profiler (SpProfilerMenuButton *self); + +G_END_DECLS + +#endif /* SP_PROFILER_MENU_BUTTON_H */ diff --git a/lib/sp-profiler.c b/lib/sp-profiler.c new file mode 100644 index 00000000..73b63884 --- /dev/null +++ b/lib/sp-profiler.c @@ -0,0 +1,983 @@ +/* sp-profiler.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 +#include +#include +#include + +#include "sp-profiler.h" + +typedef struct +{ + SpCaptureWriter *writer; + + /* All sources added */ + GPtrArray *sources; + + /* Array of GError failures */ + GPtrArray *failures; + + /* Sources currently starting */ + GPtrArray *starting; + + /* Sources currently stopping */ + GPtrArray *stopping; + + /* Sources that have failed or finished */ + GPtrArray *finished_or_failed; + + /* Pids to notify children about before prepare */ + GArray *pids; + + /* Timer for simple time tracking */ + GTimer *timer; + guint timer_notify_source; + + /* Arguments and environment variables for spawning */ + gchar **spawn_argv; + gchar **spawn_env; + + /* State flags */ + guint is_running : 1; + guint is_stopping : 1; + guint is_starting : 1; + + /* + * If we should spawn argv when starting up. This allows UI to set + * spawn argv/env but enable disable with a toggle. + */ + guint spawn : 1; + + /* If we should inherit the environment when spawning */ + guint spawn_inherit_environ : 1; + + /* + * If we should profile the entire system. Setting this results in pids + * being ignored. This is primarily useful for UI to toggle on/off the + * feature of per-process vs whole-system. + */ + guint whole_system : 1; +} SpProfilerPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SpProfiler, sp_profiler, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_IS_MUTABLE, + PROP_IS_RUNNING, + PROP_ELAPSED, + PROP_SPAWN, + PROP_SPAWN_INHERIT_ENVIRON, + PROP_WHOLE_SYSTEM, + N_PROPS +}; + +enum { + FAILED, + STOPPED, + N_SIGNALS +}; + +static GParamSpec *properties [N_PROPS]; +static guint signals [N_SIGNALS]; + +static inline gint +_g_ptr_array_find (GPtrArray *ar, + gpointer item) +{ + guint i; + + for (i = 0; i < ar->len; i++) + { + if (item == g_ptr_array_index (ar, i)) + return i; + } + + return -1; +} + +static inline gboolean +_g_ptr_array_contains (GPtrArray *ar, + gpointer item) +{ + return (-1 != _g_ptr_array_find (ar, item)); +} + +gdouble +sp_profiler_get_elapsed (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER (self), 0.0); + + return (priv->timer != NULL) ? g_timer_elapsed (priv->timer, NULL) : 0.0; +} + +static void +sp_profiler_clear_timer (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_assert (SP_IS_PROFILER (self)); + + g_clear_pointer (&priv->timer, g_timer_destroy); + + if (priv->timer_notify_source != 0) + { + g_source_remove (priv->timer_notify_source); + priv->timer_notify_source = 0; + } +} + +static void +sp_profiler_real_stopped (SpProfiler *self) +{ + g_assert (SP_IS_PROFILER (self)); + + sp_profiler_clear_timer (self); +} + +static gboolean +sp_profiler_notify_elapsed_cb (gpointer data) +{ + SpProfiler *self = data; + + g_assert (SP_IS_PROFILER (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ELAPSED]); + + return G_SOURCE_CONTINUE; +} + +static void +sp_profiler_dispose (GObject *object) +{ + SpProfiler *self = (SpProfiler *)object; + + if (sp_profiler_get_is_running (self)) + { + sp_profiler_stop (self); + return; + } + + sp_profiler_clear_timer (self); + + G_OBJECT_CLASS (sp_profiler_parent_class)->dispose (object); +} + +static void +sp_profiler_finalize (GObject *object) +{ + SpProfiler *self = (SpProfiler *)object; + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_clear_pointer (&priv->writer, sp_capture_writer_unref); + g_clear_pointer (&priv->sources, g_ptr_array_unref); + g_clear_pointer (&priv->starting, g_ptr_array_unref); + g_clear_pointer (&priv->stopping, g_ptr_array_unref); + g_clear_pointer (&priv->finished_or_failed, g_ptr_array_unref); + g_clear_pointer (&priv->pids, g_array_unref); + + G_OBJECT_CLASS (sp_profiler_parent_class)->finalize (object); +} + +static void +sp_profiler_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpProfiler *self = SP_PROFILER (object); + + switch (prop_id) + { + case PROP_IS_MUTABLE: + g_value_set_boolean (value, sp_profiler_get_is_mutable (self)); + break; + + case PROP_IS_RUNNING: + g_value_set_boolean (value, sp_profiler_get_is_running (self)); + break; + + case PROP_WHOLE_SYSTEM: + g_value_set_boolean (value, sp_profiler_get_whole_system (self)); + break; + + case PROP_SPAWN: + g_value_set_boolean (value, sp_profiler_get_spawn (self)); + break; + + case PROP_SPAWN_INHERIT_ENVIRON: + g_value_set_boolean (value, sp_profiler_get_spawn_inherit_environ (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_profiler_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpProfiler *self = SP_PROFILER (object); + + switch (prop_id) + { + case PROP_WHOLE_SYSTEM: + sp_profiler_set_whole_system (self, g_value_get_boolean (value)); + break; + + case PROP_SPAWN: + sp_profiler_set_spawn (self, g_value_get_boolean (value)); + break; + + case PROP_SPAWN_INHERIT_ENVIRON: + sp_profiler_set_spawn_inherit_environ (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_profiler_class_init (SpProfilerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = sp_profiler_dispose; + object_class->finalize = sp_profiler_finalize; + object_class->get_property = sp_profiler_get_property; + object_class->set_property = sp_profiler_set_property; + + klass->stopped = sp_profiler_real_stopped; + + /** + * SpProfiler:elapsed: + * + * This property is updated on a second basis while recording so that + * UIs can keep a timer of the elapsed time while recording. + * + * It contains a double with seconds as whole integers and fractions + * of second after the decimal point. + */ + properties [PROP_ELAPSED] = + g_param_spec_double ("elapsed", + "Elapsed Time", + "The amount of time elapsed while recording", + 0.0, + G_MAXDOUBLE, + 0.0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * SpProfiler:is-running: + * + * If the profiler has been started. Note that after being started, this + * property won't change back to %FALSE until all sources have stopped + * and notified of asynchronous completion. + */ + properties [PROP_IS_RUNNING] = + g_param_spec_boolean ("is-running", + "Is Running", + "If the profiler has been started", + FALSE, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * SpProfiler:is-mutable: + * + * This property is useful from a UI standpoint to desensitize + * configuration widgets once the profiler can no longer be modified. + */ + properties [PROP_IS_MUTABLE] = + g_param_spec_boolean ("is-mutable", + "Is Mutable", + "If the profiler can be modified", + FALSE, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SPAWN] = + g_param_spec_boolean ("spawn", + "Spawn", + "If a child should process should be spawned", + FALSE, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SPAWN_INHERIT_ENVIRON] = + g_param_spec_boolean ("spawn-inherit-environ", + "Spawn Inherit Environ", + "If a child should inherit the current environment", + FALSE, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + /** + * SpProfiler:whole-system: + * + * This property denotes if the whole system should be profiled instead of + * a single process. This is useful for UI to toggle between process + * selection and all processes. + * + * Setting this to %TRUE will result in the pids added to be ignored + * during startup. + */ + properties [PROP_WHOLE_SYSTEM] = + g_param_spec_boolean ("whole-system", + "Whole System", + "If the whole system should be profiled", + TRUE, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + signals [STOPPED] = g_signal_new ("stopped", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SpProfilerClass, stopped), + NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals [FAILED] = g_signal_new ("failed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SpProfilerClass, failed), + NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_ERROR); + +} + +static void +sp_profiler_init (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + priv->whole_system = TRUE; + + priv->failures = g_ptr_array_new_with_free_func ((GDestroyNotify)g_error_free); + priv->sources = g_ptr_array_new_with_free_func (g_object_unref); + priv->starting = g_ptr_array_new_with_free_func (g_object_unref); + priv->stopping = g_ptr_array_new_with_free_func (g_object_unref); + priv->finished_or_failed = g_ptr_array_new_with_free_func (g_object_unref); + priv->pids = g_array_new (FALSE, FALSE, sizeof (GPid)); +} + +SpProfiler * +sp_profiler_new (void) +{ + return g_object_new (SP_TYPE_PROFILER, NULL); +} + +static void +sp_profiler_finish_startup (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + guint i; + + g_assert (SP_IS_PROFILER (self)); + g_assert (priv->is_starting == TRUE); + g_assert (priv->starting->len == 0); + + sp_profiler_clear_timer (self); + + priv->timer = g_timer_new (); + + /* + * Add a source to update our watchers of elapsed time. + * We use 1000 instead of add_seconds(1) so that we are + * not subject to as much drift. + */ + priv->timer_notify_source = + g_timeout_add (1000, + sp_profiler_notify_elapsed_cb, + self); + + for (i = 0; i < priv->sources->len; i++) + { + SpSource *source = g_ptr_array_index (priv->sources, i); + + sp_source_start (source); + } + + priv->is_starting = FALSE; + + /* + * If any of the sources failed during startup, we will have a non-empty + * failures list. + */ + if (priv->failures->len > 0) + { + const GError *error = g_ptr_array_index (priv->failures, 0); + + g_object_ref (self); + g_signal_emit (self, signals [FAILED], 0, error); + sp_profiler_stop (self); + g_object_unref (self); + return; + } + + priv->is_running = TRUE; + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_RUNNING]); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_MUTABLE]); + + /* + * If all the sources are transient (in that they just generate information + * and then exit), we could be finished as soon as we complete startup. + * + * If we detect this, we stop immediately. + */ + if (priv->finished_or_failed->len == priv->sources->len) + sp_profiler_stop (self); +} + +void +sp_profiler_start (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + guint i; + + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (priv->is_running == FALSE); + g_return_if_fail (priv->is_stopping == FALSE); + g_return_if_fail (priv->is_starting == FALSE); + + if (priv->writer == NULL) + { + SpCaptureWriter *writer; + int fd; + + if ((-1 == (fd = syscall (__NR_memfd_create, "[sysprof]", 0))) || + (NULL == (writer = sp_capture_writer_new_from_fd (fd, 0)))) + { + const GError error = { + G_FILE_ERROR, + g_file_error_from_errno (errno), + (gchar *)g_strerror (errno) + }; + + if (fd != -1) + close (fd); + + g_signal_emit (self, signals [FAILED], 0, &error); + + return; + } + + sp_profiler_set_writer (self, writer); + g_clear_pointer (&writer, sp_capture_writer_unref); + } + + priv->is_running = TRUE; + priv->is_starting = TRUE; + + if (priv->failures->len > 0) + g_ptr_array_remove_range (priv->failures, 0, priv->failures->len); + + if (priv->spawn && priv->spawn_argv && priv->spawn_argv[0]) + { + g_autoptr(GPtrArray) ar = g_ptr_array_new_with_free_func (g_free); + GPid pid; + GError *error = NULL; + + if (priv->spawn_inherit_environ) + { + gchar **environ = g_get_environ (); + + for (i = 0; environ[i]; i++) + g_ptr_array_add (ar, environ[i]); + g_free (environ); + } + + if (priv->spawn_env) + { + for (i = 0; priv->spawn_env[i]; i++) + g_ptr_array_add (ar, g_strdup (priv->spawn_env[i])); + } + + g_ptr_array_add (ar, NULL); + + if (!g_spawn_async (g_get_home_dir (), + priv->spawn_argv, + (gchar **)ar->pdata, + (G_SPAWN_SEARCH_PATH | + G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDOUT_TO_DEV_NULL), + NULL, + NULL, + &pid, + &error)) + g_ptr_array_add (priv->failures, error); + else + g_array_append_val (priv->pids, pid); + } + + for (i = 0; i < priv->sources->len; i++) + { + SpSource *source = g_ptr_array_index (priv->sources, i); + guint j; + + if (priv->whole_system == FALSE) + { + for (j = 0; j < priv->pids->len; j++) + { + GPid pid = g_array_index (priv->pids, GPid, j); + + sp_source_add_pid (source, pid); + } + } + + sp_source_set_writer (source, priv->writer); + sp_source_prepare (source); + } + + for (i = 0; i < priv->sources->len; i++) + { + SpSource *source = g_ptr_array_index (priv->sources, i); + + if (!sp_source_get_is_ready (source)) + g_ptr_array_add (priv->starting, g_object_ref (source)); + } + + if (priv->starting->len == 0) + sp_profiler_finish_startup (self); +} + +static void +sp_profiler_finish_stopping (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_assert (SP_IS_PROFILER (self)); + g_assert (priv->is_starting == FALSE); + g_assert (priv->is_stopping == TRUE); + g_assert (priv->stopping->len == 0); + + if (priv->failures->len > 0) + { + const GError *error = g_ptr_array_index (priv->failures, 0); + + g_signal_emit (self, signals [FAILED], 0, error); + } + + priv->is_running = FALSE; + priv->is_stopping = FALSE; + + g_signal_emit (self, signals [STOPPED], 0); + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_RUNNING]); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_MUTABLE]); +} + +void +sp_profiler_stop (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + guint i; + + g_return_if_fail (SP_IS_PROFILER (self)); + + if (priv->is_stopping || (!priv->is_starting && !priv->is_running)) + return; + + priv->is_stopping = TRUE; + + /* + * First we add everything to the stopping list, so that we can + * be notified of when they have completed. If everything stopped + * synchronously, the stopping list will be empty after calling + * sp_source_stop() for every source. Otherwise, we need to delay + * stopping for a little bit. + */ + + for (i = 0; i < priv->sources->len; i++) + { + SpSource *source = g_ptr_array_index (priv->sources, i); + + if (!_g_ptr_array_contains (priv->finished_or_failed, source)) + g_ptr_array_add (priv->stopping, g_object_ref (source)); + } + + for (i = 0; i < priv->sources->len; i++) + { + SpSource *source = g_ptr_array_index (priv->sources, i); + + sp_source_stop (source); + } + + if (priv->is_stopping && priv->stopping->len == 0) + sp_profiler_finish_stopping (self); +} + +gboolean +sp_profiler_get_is_running (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER (self), FALSE); + + return priv->is_running; +} + +void +sp_profiler_set_writer (SpProfiler *self, + SpCaptureWriter *writer) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (priv->is_running == FALSE); + g_return_if_fail (priv->is_stopping == FALSE); + g_return_if_fail (writer != NULL); + + if (priv->writer != writer) + { + g_clear_pointer (&priv->writer, sp_capture_writer_unref); + + if (writer != NULL) + priv->writer = sp_capture_writer_ref (writer); + } +} + +static void +sp_profiler_track_completed (SpProfiler *self, + SpSource *source) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + gint i; + + g_assert (SP_IS_PROFILER (self)); + g_assert (SP_IS_SOURCE (source)); + + if (!_g_ptr_array_contains (priv->finished_or_failed, source)) + g_ptr_array_add (priv->finished_or_failed, g_object_ref (source)); + + if (priv->is_starting) + { + i = _g_ptr_array_find (priv->starting, source); + + if (i >= 0) + { + g_ptr_array_remove_index (priv->starting, i); + if (priv->starting->len == 0) + sp_profiler_finish_startup (self); + } + } + + if (priv->is_stopping) + { + i = _g_ptr_array_find (priv->stopping, source); + + if (i >= 0) + { + g_ptr_array_remove_index_fast (priv->stopping, i); + + if ((priv->is_stopping == TRUE) && (priv->stopping->len == 0)) + sp_profiler_finish_stopping (self); + } + } + + if (!priv->is_starting) + { + if (priv->finished_or_failed->len == priv->sources->len) + sp_profiler_stop (self); + } +} + +static void +sp_profiler_source_finished (SpProfiler *self, + SpSource *source) +{ + g_assert (SP_IS_PROFILER (self)); + g_assert (SP_IS_SOURCE (source)); + + sp_profiler_track_completed (self, source); +} + +static void +sp_profiler_source_ready (SpProfiler *self, + SpSource *source) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + guint i; + + g_assert (SP_IS_PROFILER (self)); + g_assert (SP_IS_SOURCE (source)); + + for (i = 0; i < priv->starting->len; i++) + { + SpSource *ele = g_ptr_array_index (priv->starting, i); + + if (ele == source) + { + g_ptr_array_remove_index_fast (priv->starting, i); + + if ((priv->is_starting == TRUE) && (priv->starting->len == 0)) + sp_profiler_finish_startup (self); + + break; + } + } +} + +static void +sp_profiler_source_failed (SpProfiler *self, + const GError *reason, + SpSource *source) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_assert (SP_IS_PROFILER (self)); + g_assert (reason != NULL); + g_assert (SP_IS_SOURCE (source)); + + sp_profiler_track_completed (self, source); + + /* Failure emitted out of band */ + if (!priv->is_starting && !priv->is_stopping && !priv->is_running) + return; + + g_ptr_array_add (priv->failures, g_error_copy (reason)); + + /* Ignore during start/stop, we handle this in other places */ + if (priv->is_starting || priv->is_stopping) + return; + + if (priv->is_running) + sp_profiler_stop (self); +} + +void +sp_profiler_add_source (SpProfiler *self, + SpSource *source) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (SP_IS_SOURCE (source)); + g_return_if_fail (priv->is_running == FALSE); + g_return_if_fail (priv->is_starting == FALSE); + g_return_if_fail (priv->is_stopping == FALSE); + + g_signal_connect_object (source, + "failed", + G_CALLBACK (sp_profiler_source_failed), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (source, + "finished", + G_CALLBACK (sp_profiler_source_finished), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (source, + "ready", + G_CALLBACK (sp_profiler_source_ready), + self, + G_CONNECT_SWAPPED); + + + g_ptr_array_add (priv->sources, g_object_ref (source)); +} + +void +sp_profiler_add_pid (SpProfiler *self, + GPid pid) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (pid > -1); + g_return_if_fail (priv->is_starting == FALSE); + g_return_if_fail (priv->is_stopping == FALSE); + g_return_if_fail (priv->is_running == FALSE); + + g_array_append_val (priv->pids, pid); +} + +void +sp_profiler_remove_pid (SpProfiler *self, + GPid pid) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + guint i; + + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (pid > -1); + g_return_if_fail (priv->is_starting == FALSE); + g_return_if_fail (priv->is_stopping == FALSE); + g_return_if_fail (priv->is_running == FALSE); + + for (i = 0; i < priv->pids->len; i++) + { + GPid ele = g_array_index (priv->pids, GPid, i); + + if (ele == pid) + { + g_array_remove_index_fast (priv->pids, i); + break; + } + } +} + +gboolean +sp_profiler_get_is_mutable (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER (self), FALSE); + + return !(priv->is_starting || priv->is_stopping || priv->is_running); +} + +gboolean +sp_profiler_get_whole_system (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER (self), FALSE); + + return priv->whole_system; +} + +void +sp_profiler_set_whole_system (SpProfiler *self, + gboolean whole_system) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER (self)); + + whole_system = !!whole_system; + + if (whole_system != priv->whole_system) + { + priv->whole_system = whole_system; + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_WHOLE_SYSTEM]); + } +} + +const GPid * +sp_profiler_get_pids (SpProfiler *self, + guint *n_pids) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER (self), NULL); + g_return_val_if_fail (n_pids != NULL, NULL); + + *n_pids = priv->pids->len; + + return (GPid *)(gpointer)priv->pids->data; +} + +/** + * sp_profiler_get_writer: + * + * Returns: (nullable) (transfer none): An #SpCaptureWriter or %NULL. + */ +SpCaptureWriter * +sp_profiler_get_writer (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER (self), NULL); + + return priv->writer; +} + +gboolean +sp_profiler_get_spawn (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER (self), FALSE); + + return priv->spawn; +} + +void +sp_profiler_set_spawn (SpProfiler *self, + gboolean spawn) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER (self)); + + spawn = !!spawn; + + if (priv->spawn != spawn) + { + priv->spawn = spawn; + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SPAWN]); + } +} + +gboolean +sp_profiler_get_spawn_inherit_environ (SpProfiler *self) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_val_if_fail (SP_IS_PROFILER (self), FALSE); + + return priv->spawn_inherit_environ; +} + +void +sp_profiler_set_spawn_inherit_environ (SpProfiler *self, + gboolean spawn_inherit_environ) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER (self)); + + spawn_inherit_environ = !!spawn_inherit_environ; + + if (priv->spawn_inherit_environ != spawn_inherit_environ) + { + priv->spawn_inherit_environ = spawn_inherit_environ; + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SPAWN_INHERIT_ENVIRON]); + } +} + +void +sp_profiler_set_spawn_argv (SpProfiler *self, + const gchar * const *spawn_argv) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER (self)); + + g_strfreev (priv->spawn_argv); + priv->spawn_argv = g_strdupv ((gchar **)spawn_argv); +} + +void +sp_profiler_set_spawn_env (SpProfiler *self, + const gchar * const *spawn_env) +{ + SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + + g_return_if_fail (SP_IS_PROFILER (self)); + + g_strfreev (priv->spawn_env); + priv->spawn_env = g_strdupv ((gchar **)spawn_env); +} diff --git a/lib/sp-profiler.h b/lib/sp-profiler.h new file mode 100644 index 00000000..34663ea5 --- /dev/null +++ b/lib/sp-profiler.h @@ -0,0 +1,92 @@ +/* sp-profiler.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_PROFILER_H +#define SP_PROFILER_H + +#include "sp-capture-writer.h" +#include "sp-source.h" + +G_BEGIN_DECLS + +#define SP_TYPE_PROFILER (sp_profiler_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpProfiler, sp_profiler, SP, PROFILER, GObject) + +struct _SpProfilerClass +{ + GObjectClass parent_class; + + /** + * SpProfiler::failed: + * @self: A #SpProfiler + * @reason: A #GError representing the reason for the failure + * + * This signal is emitted if the profiler failed. Note that + * #SpProfiler::stopped will also be emitted, but does not allow for + * receiving the error condition. + */ + void (*failed) (SpProfiler *self, + const GError *error); + + /** + * SpProfiler::stopped: + * @self: A #SpProfiler + * + * This signal is emitted when a profiler is stopped. It will always be + * emitted after a sp_profiler_start() has been called, either after + * completion of sp_profiler_stop() or after a failure or after asynchronous + * completion of stopping. + */ + void (*stopped) (SpProfiler *self); +}; + +SpProfiler *sp_profiler_new (void); +gdouble sp_profiler_get_elapsed (SpProfiler *self); +void sp_profiler_add_source (SpProfiler *self, + SpSource *source); +void sp_profiler_set_writer (SpProfiler *self, + SpCaptureWriter *writer); +SpCaptureWriter *sp_profiler_get_writer (SpProfiler *self); +gboolean sp_profiler_get_is_running (SpProfiler *self); +void sp_profiler_start (SpProfiler *self); +void sp_profiler_stop (SpProfiler *self); +void sp_profiler_add_pid (SpProfiler *self, + GPid pid); +void sp_profiler_remove_pid (SpProfiler *self, + GPid pid); +gboolean sp_profiler_get_is_mutable (SpProfiler *self); +gboolean sp_profiler_get_whole_system (SpProfiler *self); +void sp_profiler_set_whole_system (SpProfiler *self, + gboolean whole_system); +const GPid *sp_profiler_get_pids (SpProfiler *self, + guint *n_pids); +gboolean sp_profiler_get_spawn (SpProfiler *self); +void sp_profiler_set_spawn (SpProfiler *self, + gboolean spawn); +void sp_profiler_set_spawn_argv (SpProfiler *self, + const gchar * const *spawn_argv); +void sp_profiler_set_spawn_env (SpProfiler *self, + const gchar * const *spawn_env); +gboolean sp_profiler_get_spawn_inherit_environ (SpProfiler *self); +void sp_profiler_set_spawn_inherit_environ (SpProfiler *self, + gboolean spawn_inherit_environ); + +G_END_DECLS + +#endif /* SP_PROFILER_H */ diff --git a/lib/sp-recording-state-view.c b/lib/sp-recording-state-view.c new file mode 100644 index 00000000..6762b5eb --- /dev/null +++ b/lib/sp-recording-state-view.c @@ -0,0 +1,193 @@ +/* sp-recording-state-view.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-recording-state-view.h" + +typedef struct +{ + SpProfiler *profiler; + gulong notify_elapsed_handler; + GtkLabel *elapsed; +} SpRecordingStateViewPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SpRecordingStateView, sp_recording_state_view, GTK_TYPE_BIN) + +enum { + PROP_0, + PROP_PROFILER, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +GtkWidget * +sp_recording_state_view_new (void) +{ + return g_object_new (SP_TYPE_RECORDING_STATE_VIEW, NULL); +} + +static void +sp_recording_state_view_notify_elapsed (SpRecordingStateView *self, + GParamSpec *pspec, + SpProfiler *profiler) +{ + SpRecordingStateViewPrivate *priv = sp_recording_state_view_get_instance_private (self); + g_autofree gchar *str = NULL; + gint64 elapsed; + guint hours; + guint minutes; + guint seconds; + + g_assert (SP_IS_RECORDING_STATE_VIEW (self)); + g_assert (SP_IS_PROFILER (profiler)); + + elapsed = (gint64)sp_profiler_get_elapsed (profiler); + + hours = elapsed / (60 * 60); + if (hours > 0) + minutes = (elapsed % (hours * 60 * 60)) / 60; + else + minutes = elapsed / 60; + seconds = elapsed % 60; + + if (hours == 0) + str = g_strdup_printf ("%02u:%02u", minutes, seconds); + else + str = g_strdup_printf ("%02u:%02u:%02u", hours, minutes, seconds); + + gtk_label_set_label (priv->elapsed, str); +} + +static void +sp_recording_state_view_destroy (GtkWidget *widget) +{ + SpRecordingStateView *self = (SpRecordingStateView *)widget; + SpRecordingStateViewPrivate *priv = sp_recording_state_view_get_instance_private (self); + + if (priv->profiler != NULL) + { + g_signal_handler_disconnect (priv->profiler, priv->notify_elapsed_handler); + g_clear_object (&priv->profiler); + } + + GTK_WIDGET_CLASS (sp_recording_state_view_parent_class)->destroy (widget); +} + +static void +sp_recording_state_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpRecordingStateView *self = SP_RECORDING_STATE_VIEW (object); + SpRecordingStateViewPrivate *priv = sp_recording_state_view_get_instance_private (self); + + switch (prop_id) + { + case PROP_PROFILER: + g_value_set_object (value, priv->profiler); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_recording_state_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpRecordingStateView *self = SP_RECORDING_STATE_VIEW (object); + + switch (prop_id) + { + case PROP_PROFILER: + sp_recording_state_view_set_profiler (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_recording_state_view_class_init (SpRecordingStateViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = sp_recording_state_view_get_property; + object_class->set_property = sp_recording_state_view_set_property; + + widget_class->destroy = sp_recording_state_view_destroy; + + properties [PROP_PROFILER] = + g_param_spec_object ("profiler", + "Profiler", + "Profiler", + SP_TYPE_PROFILER, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/sysprof/ui/sp-recording-state-view.ui"); + gtk_widget_class_bind_template_child_private (widget_class, SpRecordingStateView, elapsed); +} + +static void +sp_recording_state_view_init (SpRecordingStateView *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +void +sp_recording_state_view_set_profiler (SpRecordingStateView *self, + SpProfiler *profiler) +{ + SpRecordingStateViewPrivate *priv = sp_recording_state_view_get_instance_private (self); + + g_assert (SP_IS_RECORDING_STATE_VIEW (self)); + g_assert (!profiler || SP_IS_PROFILER (profiler)); + + gtk_label_set_label (priv->elapsed, "00:00"); + + if (profiler != priv->profiler) + { + if (priv->profiler != NULL) + { + g_signal_handler_disconnect (priv->profiler, priv->notify_elapsed_handler); + g_clear_object (&priv->profiler); + } + + gtk_label_set_label (priv->elapsed, "00:00"); + + if (profiler != NULL) + { + priv->profiler = g_object_ref (profiler); + priv->notify_elapsed_handler = + g_signal_connect_object (profiler, + "notify::elapsed", + G_CALLBACK (sp_recording_state_view_notify_elapsed), + self, + G_CONNECT_SWAPPED); + } + } +} diff --git a/lib/sp-recording-state-view.h b/lib/sp-recording-state-view.h new file mode 100644 index 00000000..37b3b82f --- /dev/null +++ b/lib/sp-recording-state-view.h @@ -0,0 +1,43 @@ +/* sp-recording-state-view.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_RECORDING_STATE_VIEW_H +#define SP_RECORDING_STATE_VIEW_H + +#include + +#include "sp-profiler.h" + +G_BEGIN_DECLS + +#define SP_TYPE_RECORDING_STATE_VIEW (sp_recording_state_view_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpRecordingStateView, sp_recording_state_view, SP, RECORDING_STATE_VIEW, GtkBin) + +struct _SpRecordingStateViewClass +{ + GtkBinClass parent; +}; + +GtkWidget *sp_recording_state_view_new (void); +void sp_recording_state_view_set_profiler (SpRecordingStateView *self, + SpProfiler *profiler); + +G_END_DECLS + +#endif /* SP_RECORDING_STATE_VIEW_H */ diff --git a/lib/sp-scrolled-window.c b/lib/sp-scrolled-window.c new file mode 100644 index 00000000..82c8ce25 --- /dev/null +++ b/lib/sp-scrolled-window.c @@ -0,0 +1,280 @@ +/* sp-scrolled-window.c + * + * Copyright (C) 2014 Christian Hergert + * + * 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 "sp-scrolled-window.h" + +typedef struct +{ + gint max_content_height; + gint max_content_width; +} SpScrolledWindowPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SpScrolledWindow, sp_scrolled_window, GTK_TYPE_SCROLLED_WINDOW) + +enum { + PROP_0, + PROP_MAX_CONTENT_HEIGHT, + PROP_MAX_CONTENT_WIDTH, + LAST_PROP +}; + +static GParamSpec *properties [LAST_PROP]; + +GtkWidget * +sp_scrolled_window_new (void) +{ + return g_object_new (SP_TYPE_SCROLLED_WINDOW, NULL); +} + +gint +sp_scrolled_window_get_max_content_height (SpScrolledWindow *self) +{ + SpScrolledWindowPrivate *priv = sp_scrolled_window_get_instance_private (self); + + g_return_val_if_fail (SP_IS_SCROLLED_WINDOW (self), -1); + + return priv->max_content_height; +} + +/** + * sp_scrolled_window_set_max_content_height: + * @max_content_height: the max allowed height request or -1 to ignore. + * + * This function will set the "max-content-height" property. This property is + * used to determine the maximum height that the scrolled window will request. + * + * This is useful if you want to have a scrolled window grow with the child + * allocation, but only up to a certain height. + */ +void +sp_scrolled_window_set_max_content_height (SpScrolledWindow *self, + gint max_content_height) +{ + SpScrolledWindowPrivate *priv = sp_scrolled_window_get_instance_private (self); + + g_return_if_fail (SP_IS_SCROLLED_WINDOW (self)); + + if (max_content_height != priv->max_content_height) + { + priv->max_content_height = max_content_height; + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_CONTENT_HEIGHT]); + gtk_widget_queue_resize (GTK_WIDGET (self)); + } +} + +gint +sp_scrolled_window_get_max_content_width (SpScrolledWindow *self) +{ + SpScrolledWindowPrivate *priv = sp_scrolled_window_get_instance_private (self); + + g_return_val_if_fail (SP_IS_SCROLLED_WINDOW (self), -1); + + return priv->max_content_width; +} + +/** + * sp_scrolled_window_set_max_content_width: + * @max_content_width: the max allowed width request or -1 to ignore. + * + * This function will set the "max-content-width" property. This property is + * used to determine the maximum width that the scrolled window will request. + * + * This is useful if you want to have a scrolled window grow with the child + * allocation, but only up to a certain width. + */ +void +sp_scrolled_window_set_max_content_width (SpScrolledWindow *self, + gint max_content_width) +{ + SpScrolledWindowPrivate *priv = sp_scrolled_window_get_instance_private (self); + + g_return_if_fail (SP_IS_SCROLLED_WINDOW (self)); + + if (max_content_width != priv->max_content_width) + { + priv->max_content_width = max_content_width; + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_CONTENT_HEIGHT]); + gtk_widget_queue_resize (GTK_WIDGET (self)); + } +} + +static void +sp_scrolled_window_get_preferred_height (GtkWidget *widget, + gint *minimum_height, + gint *natural_height) +{ + SpScrolledWindow *self = (SpScrolledWindow *)widget; + SpScrolledWindowPrivate *priv = sp_scrolled_window_get_instance_private (self); + + g_return_if_fail (SP_IS_SCROLLED_WINDOW (self)); + + GTK_WIDGET_CLASS (sp_scrolled_window_parent_class)->get_preferred_height (widget, minimum_height, natural_height); + + if (natural_height) + { + if (priv->max_content_height > -1) + { + GtkWidget *child; + GtkStyleContext *style; + GtkBorder border; + gint child_min_height; + gint child_nat_height; + gint additional; + + if (!(child = gtk_bin_get_child (GTK_BIN (widget)))) + return; + + style = gtk_widget_get_style_context (widget); + gtk_style_context_get_border (style, gtk_style_context_get_state (style), &border); + additional = border.top + border.bottom; + + gtk_widget_get_preferred_height (child, &child_min_height, &child_nat_height); + + if ((child_nat_height > *natural_height) && (priv->max_content_height > *natural_height)) + *natural_height = MIN (priv->max_content_height, child_nat_height) + additional; + } + } +} + +static void +sp_scrolled_window_get_preferred_width (GtkWidget *widget, + gint *minimum_width, + gint *natural_width) +{ + SpScrolledWindow *self = (SpScrolledWindow *)widget; + SpScrolledWindowPrivate *priv = sp_scrolled_window_get_instance_private (self); + + g_return_if_fail (SP_IS_SCROLLED_WINDOW (self)); + + GTK_WIDGET_CLASS (sp_scrolled_window_parent_class)->get_preferred_width (widget, minimum_width, natural_width); + + if (natural_width) + { + if (priv->max_content_width > -1) + { + GtkWidget *child; + GtkStyleContext *style; + GtkBorder border; + gint child_min_width; + gint child_nat_width; + gint additional; + + if (!(child = gtk_bin_get_child (GTK_BIN (widget)))) + return; + + style = gtk_widget_get_style_context (widget); + gtk_style_context_get_border (style, gtk_style_context_get_state (style), &border); + additional = border.left = border.right + 1; + + gtk_widget_get_preferred_width (child, &child_min_width, &child_nat_width); + + if ((child_nat_width > *natural_width) && (priv->max_content_width > *natural_width)) + *natural_width = MIN (priv->max_content_width, child_nat_width) + additional; + } + } +} + +static void +sp_scrolled_window_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpScrolledWindow *self = SP_SCROLLED_WINDOW (object); + + switch (prop_id) + { + case PROP_MAX_CONTENT_HEIGHT: + g_value_set_int (value, sp_scrolled_window_get_max_content_height (self)); + break; + + case PROP_MAX_CONTENT_WIDTH: + g_value_set_int (value, sp_scrolled_window_get_max_content_width (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_scrolled_window_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpScrolledWindow *self = SP_SCROLLED_WINDOW (object); + + switch (prop_id) + { + case PROP_MAX_CONTENT_HEIGHT: + sp_scrolled_window_set_max_content_height (self, g_value_get_int (value)); + break; + + case PROP_MAX_CONTENT_WIDTH: + sp_scrolled_window_set_max_content_width (self, g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_scrolled_window_class_init (SpScrolledWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = sp_scrolled_window_get_property; + object_class->set_property = sp_scrolled_window_set_property; + + widget_class->get_preferred_width = sp_scrolled_window_get_preferred_width; + widget_class->get_preferred_height = sp_scrolled_window_get_preferred_height; + + properties [PROP_MAX_CONTENT_HEIGHT] = + g_param_spec_int ("max-content-height", + "Max Content Height", + "The maximum height request that can be made.", + -1, + G_MAXINT, + -1, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_MAX_CONTENT_WIDTH] = + g_param_spec_int ("max-content-width", + "Max Content Width", + "The maximum width request that can be made.", + -1, + G_MAXINT, + -1, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, LAST_PROP, properties); +} + +static void +sp_scrolled_window_init (SpScrolledWindow *self) +{ + SpScrolledWindowPrivate *priv = sp_scrolled_window_get_instance_private (self); + + priv->max_content_height = -1; + priv->max_content_width = -1; +} diff --git a/lib/sp-scrolled-window.h b/lib/sp-scrolled-window.h new file mode 100644 index 00000000..4b636d30 --- /dev/null +++ b/lib/sp-scrolled-window.h @@ -0,0 +1,45 @@ +/* sp-scrolled-window.h + * + * Copyright (C) 2014 Christian Hergert + * + * 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 SP_SCROLLED_WINDOW_H +#define SP_SCROLLED_WINDOW_H + +#include + +G_BEGIN_DECLS + +#define SP_TYPE_SCROLLED_WINDOW (sp_scrolled_window_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpScrolledWindow, sp_scrolled_window, SP, SCROLLED_WINDOW, GtkScrolledWindow) + +struct _SpScrolledWindowClass +{ + GtkScrolledWindowClass parent_class; +}; + +GtkWidget *sp_scrolled_window_new (void); +gint sp_scrolled_window_get_max_content_height (SpScrolledWindow *self); +void sp_scrolled_window_set_max_content_height (SpScrolledWindow *self, + gint max_content_height); +gint sp_scrolled_window_get_max_content_width (SpScrolledWindow *self); +void sp_scrolled_window_set_max_content_width (SpScrolledWindow *self, + gint max_content_width); + +G_END_DECLS + +#endif /* SP_SCROLLED_WINDOW_H */ diff --git a/lib/sp-source.c b/lib/sp-source.c new file mode 100644 index 00000000..6a3225cc --- /dev/null +++ b/lib/sp-source.c @@ -0,0 +1,137 @@ +/* sp-source.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-source.h" + +G_DEFINE_INTERFACE (SpSource, sp_source, G_TYPE_OBJECT) + +enum { + FAILED, + FINISHED, + READY, + N_SIGNALS +}; + +static guint signals [N_SIGNALS]; + +static void +sp_source_default_init (SpSourceInterface *iface) +{ + signals [FAILED] = g_signal_new ("failed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_ERROR); + + signals [FINISHED] = g_signal_new ("finished", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals [READY] = g_signal_new ("ready", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +void +sp_source_add_pid (SpSource *self, + GPid pid) +{ + g_return_if_fail (SP_IS_SOURCE (self)); + g_return_if_fail (pid != FALSE); + + if (SP_SOURCE_GET_IFACE (self)->add_pid) + SP_SOURCE_GET_IFACE (self)->add_pid (self, pid); +} + +void +sp_source_emit_finished (SpSource *self) +{ + g_return_if_fail (SP_IS_SOURCE (self)); + + g_signal_emit (self, signals [FINISHED], 0); +} + +void +sp_source_emit_failed (SpSource *self, + const GError *error) +{ + g_return_if_fail (SP_IS_SOURCE (self)); + g_return_if_fail (error != NULL); + + g_signal_emit (self, signals [FAILED], 0, error); +} + +void +sp_source_emit_ready (SpSource *self) +{ + g_return_if_fail (SP_IS_SOURCE (self)); + + g_signal_emit (self, signals [READY], 0); +} + +gboolean +sp_source_get_is_ready (SpSource *self) +{ + g_return_val_if_fail (SP_IS_SOURCE (self), FALSE); + + if (SP_SOURCE_GET_IFACE (self)->get_is_ready) + return SP_SOURCE_GET_IFACE (self)->get_is_ready (self); + + return TRUE; +} + +void +sp_source_prepare (SpSource *self) +{ + g_return_if_fail (SP_IS_SOURCE (self)); + + if (SP_SOURCE_GET_IFACE (self)->prepare) + SP_SOURCE_GET_IFACE (self)->prepare (self); +} + +void +sp_source_set_writer (SpSource *self, + SpCaptureWriter *writer) +{ + g_return_if_fail (SP_IS_SOURCE (self)); + g_return_if_fail (writer != NULL); + + if (SP_SOURCE_GET_IFACE (self)->set_writer) + SP_SOURCE_GET_IFACE (self)->set_writer (self, writer); +} + +void +sp_source_start (SpSource *self) +{ + g_return_if_fail (SP_IS_SOURCE (self)); + + if (SP_SOURCE_GET_IFACE (self)->start) + SP_SOURCE_GET_IFACE (self)->start (self); +} + +void +sp_source_stop (SpSource *self) +{ + g_return_if_fail (SP_IS_SOURCE (self)); + + if (SP_SOURCE_GET_IFACE (self)->stop) + SP_SOURCE_GET_IFACE (self)->stop (self); +} diff --git a/lib/sp-source.h b/lib/sp-source.h new file mode 100644 index 00000000..2a337a52 --- /dev/null +++ b/lib/sp-source.h @@ -0,0 +1,133 @@ +/* sp-source.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_SOURCE_H +#define SP_SOURCE_H + +#include + +#include "sp-capture-writer.h" + +G_BEGIN_DECLS + +#define SP_TYPE_SOURCE (sp_source_get_type()) + +G_DECLARE_INTERFACE (SpSource, sp_source, SP, SOURCE, GObject) + +struct _SpSourceInterface +{ + GTypeInterface parent_iface; + + /** + * SpSource::get_is_ready: + * @self: A SpSource. + * + * This function should return %TRUE if the source is ready to start + * profiling. If the source is not ready until after sp_source_start() has + * been called, use sp_source_emit_ready() to notify the profiler that the + * source is ready for profiling. + * + * Returns: %TRUE if the source is ready to start profiling. + */ + gboolean (*get_is_ready) (SpSource *self); + + /** + * SpSource::set_writer: + * @self: A #SpSource. + * @writer: A #SpCaptureWriter + * + * Sets the #SpCaptureWriter to use when profiling. @writer is only safe to + * use from the main thread. If you need to capture from a thread, you should + * create a memory-based #SpCaptureWriter and then splice that into this + * writer from the main thread when profiling completes. + * + * See sp_capture_writer_splice() for information on splicing writers. + */ + void (*set_writer) (SpSource *self, + SpCaptureWriter *writer); + + /** + * SpSource::prepare: + * + * This function is called before profiling has started. The source should + * prepare any pre-profiling setup here. It may perform this work + * asynchronously, but must g_object_notify() the SpSource::is-ready + * property once that asynchronous work has been performed. Until it + * is ready, #SpSource::is-ready must return FALSE. + */ + void (*prepare) (SpSource *self); + + /** + * SpSource::add_pid: + * @self: A #SpSource + * @pid: A pid_t > -1 + * + * This function is used to notify the #SpSource that a new process, + * identified by @pid, should be profiled. By default, sources should + * assume all processes, and only restrict to a given set of pids if + * this function is called. + */ + void (*add_pid) (SpSource *self, + GPid pid); + + /** + * SpSource::start: + * @self: A #SpSource. + * + * Start profiling as configured. + * + * If a failure occurs while processing, the source should notify the + * profiling session via sp_source_emit_failed() from the main thread. + */ + void (*start) (SpSource *self); + + /** + * SpSource::stop: + * @self: A #SpSource. + * + * Stop capturing a profile. The source should immediately stop + * profiling and perform any cleanup tasks required. If doing + * off-main-thread capturing, this is a good time to splice your + * capture into the capture file set with sp_source_set_writer(). + * + * If you need to perform asynchronous cleanup, call + * sp_source_emit_finished() once that work has completed. If you do + * not need to perform asynchronous cleanup, call + * sp_source_emit_finished() from this function. + * + * sp_source_emit_finished() must be called from the main-thread. + */ + void (*stop) (SpSource *self); +}; + +void sp_source_add_pid (SpSource *self, + GPid pid); +void sp_source_emit_ready (SpSource *self); +void sp_source_emit_finished (SpSource *self); +void sp_source_emit_failed (SpSource *self, + const GError *error); +gboolean sp_source_get_is_ready (SpSource *self); +void sp_source_prepare (SpSource *self); +void sp_source_set_writer (SpSource *self, + SpCaptureWriter *writer); +void sp_source_start (SpSource *self); +void sp_source_stop (SpSource *self); + +G_END_DECLS + +#endif /* SP_SOURCE_H */ diff --git a/lib/sp-symbol-resolver.c b/lib/sp-symbol-resolver.c new file mode 100644 index 00000000..7e20c1a5 --- /dev/null +++ b/lib/sp-symbol-resolver.c @@ -0,0 +1,72 @@ +/* sp-symbol-resolver.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-symbol-resolver.h" + +G_DEFINE_INTERFACE (SpSymbolResolver, sp_symbol_resolver, G_TYPE_OBJECT) + +static void +sp_symbol_resolver_default_init (SpSymbolResolverInterface *iface) +{ +} + +void +sp_symbol_resolver_load (SpSymbolResolver *self, + SpCaptureReader *reader) +{ + g_return_if_fail (SP_IS_SYMBOL_RESOLVER (self)); + g_return_if_fail (reader != NULL); + + if (SP_SYMBOL_RESOLVER_GET_IFACE (self)->load) + SP_SYMBOL_RESOLVER_GET_IFACE (self)->load (self, reader); +} + +/** + * sp_symbol_resolver_resolve: + * @self: A #SpSymbolResolver + * @time: The time of the sample + * @pid: The process generating the sample + * @address: the sample address + * @tag: (out): A tag for the symbol. + * + * Gets the symbol name for @address that was part of process @pid + * at @time. Optionally, you can set @tag to a quark describing the + * symbol. This can be used to provide a bit more information when + * rendering the treeview. You might choose to describe the library + * such as "GObject" or "GTK+" or "Linux" for the kernel. + * + * Returns: (nullable) (transfer full): A newly allocated string, or %NULL. + */ +gchar * +sp_symbol_resolver_resolve (SpSymbolResolver *self, + guint64 time, + GPid pid, + SpCaptureAddress address, + GQuark *tag) +{ + GQuark dummy; + + g_return_val_if_fail (SP_IS_SYMBOL_RESOLVER (self), NULL); + + if (tag == NULL) + tag = &dummy; + + *tag = 0; + + return SP_SYMBOL_RESOLVER_GET_IFACE (self)->resolve (self, time, pid, address, tag); +} diff --git a/lib/sp-symbol-resolver.h b/lib/sp-symbol-resolver.h new file mode 100644 index 00000000..3a82205a --- /dev/null +++ b/lib/sp-symbol-resolver.h @@ -0,0 +1,55 @@ +/* sp-symbol-resolver.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_SYMBOL_RESOLVER_H +#define SP_SYMBOL_RESOLVER_H + +#include + +#include "sp-capture-reader.h" + +G_BEGIN_DECLS + +#define SP_TYPE_SYMBOL_RESOLVER (sp_symbol_resolver_get_type()) + +G_DECLARE_INTERFACE (SpSymbolResolver, sp_symbol_resolver, SP, SYMBOL_RESOLVER, GObject) + +struct _SpSymbolResolverInterface +{ + GTypeInterface parent_interface; + + void (*load) (SpSymbolResolver *self, + SpCaptureReader *reader); + gchar *(*resolve) (SpSymbolResolver *self, + guint64 time, + GPid pid, + SpCaptureAddress address, + GQuark *tag); +}; + +void sp_symbol_resolver_load (SpSymbolResolver *self, + SpCaptureReader *reader); +gchar *sp_symbol_resolver_resolve (SpSymbolResolver *self, + guint64 time, + GPid pid, + SpCaptureAddress address, + GQuark *tag); + +G_END_DECLS + +#endif /* SP_SYMBOL_RESOLVER_H */ diff --git a/lib/sysprof-version.h.in b/lib/sysprof-version.h.in new file mode 100644 index 00000000..869dbb03 --- /dev/null +++ b/lib/sysprof-version.h.in @@ -0,0 +1,97 @@ +/* sysprof-version.h.in + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SYSPROF_VERSION_H +#define SYSPROF_VERSION_H + +#if !defined(SYSPROF_INSIDE) && !defined(SYSPROF_COMPILATION) +# error "Only can be included directly." +#endif + +/** + * SECTION:sysprof-version + * @short_description: sysprof version checking + * + * sysprof provides macros to check the version of the library + * at compile-time + */ + +/** + * SYSPROF_MAJOR_VERSION: + * + * sysprof major version component (e.g. 1 if %SYSPROF_VERSION is 1.2.3) + */ +#define SYSPROF_MAJOR_VERSION (@MAJOR_VERSION@) + +/** + * SYSPROF_MINOR_VERSION: + * + * sysprof minor version component (e.g. 2 if %SYSPROF_VERSION is 1.2.3) + */ +#define SYSPROF_MINOR_VERSION (@MINOR_VERSION@) + +/** + * SYSPROF_MICRO_VERSION: + * + * sysprof micro version component (e.g. 3 if %SYSPROF_VERSION is 1.2.3) + */ +#define SYSPROF_MICRO_VERSION (@MICRO_VERSION@) + +/** + * SYSPROF_VERSION + * + * sysprof version. + */ +#define SYSPROF_VERSION (@VERSION@) + +/** + * SYSPROF_VERSION_S: + * + * sysprof version, encoded as a string, useful for printing and + * concatenation. + */ +#define SYSPROF_VERSION_S "@VERSION@" + +#define SYSPROF_ENCODE_VERSION(major,minor,micro) \ + ((major) << 24 | (minor) << 16 | (micro) << 8) + +/** + * SYSPROF_VERSION_HEX: + * + * sysprof version, encoded as an hexadecimal number, useful for + * integer comparisons. + */ +#define SYSPROF_VERSION_HEX \ + (SYSPROF_ENCODE_VERSION (SYSPROF_MAJOR_VERSION, SYSPROF_MINOR_VERSION, SYSPROF_MICRO_VERSION)) + +/** + * SYSPROF_CHECK_VERSION: + * @major: required major version + * @minor: required minor version + * @micro: required micro version + * + * Compile-time version checking. Evaluates to %TRUE if the version + * of sysprof is greater than the required one. + */ +#define SYSPROF_CHECK_VERSION(major,minor,micro) \ + (SYSPROF_MAJOR_VERSION > (major) || \ + (SYSPROF_MAJOR_VERSION == (major) && SYSPROF_MINOR_VERSION > (minor)) || \ + (SYSPROF_MAJOR_VERSION == (major) && SYSPROF_MINOR_VERSION == (minor) && \ + SYSPROF_MICRO_VERSION >= (micro))) + +#endif /* SYSPROF_VERSION_H */ diff --git a/lib/sysprof.h b/lib/sysprof.h new file mode 100644 index 00000000..776bb26a --- /dev/null +++ b/lib/sysprof.h @@ -0,0 +1,59 @@ +/* sysprof.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SYSPROF_H +#define SYSPROF_H + +#include + +G_BEGIN_DECLS + +#define SYSPROF_INSIDE +# include "sp-address.h" +# include "sp-callgraph-profile.h" +# include "sp-callgraph-view.h" +# include "sp-capture-reader.h" +# include "sp-capture-writer.h" +# include "sp-cell-renderer-percent.h" +# include "sp-clock.h" +# include "sp-elf-symbol-resolver.h" +# include "sp-empty-state-view.h" +# include "sp-error.h" +# include "sp-gjs-source.h" +# include "sp-jitmap-symbol-resolver.h" +# include "sp-kernel-symbol.h" +# include "sp-kernel-symbol-resolver.h" +# include "sp-map-lookaside.h" +# include "sp-model-filter.h" +# include "sp-recording-state-view.h" +# include "sp-perf-source.h" +# include "sp-proc-source.h" +# include "sp-process-model.h" +# include "sp-process-model-item.h" +# include "sp-process-model-row.h" +# include "sp-profile.h" +# include "sp-profiler.h" +# include "sp-profiler-menu-button.h" +# include "sp-source.h" +# include "sp-symbol-resolver.h" +# include "sysprof-version.h" +#undef SYSPROF_INSIDE + +G_END_DECLS + +#endif /* SYSPROF_H */ diff --git a/lib/testdemangle.c b/lib/testdemangle.c deleted file mode 100644 index c2b3d56f..00000000 --- a/lib/testdemangle.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "elfparser.h" - -int -main () -{ - printf ("%s\n", elf_demangle ("_ZN8Inkscape7FiltersL12filter2D_FIRIhLj4EEEvPT_iiPKS2_iiiiPKNS_4Util10FixedPointIjLj16EEEii")); -} diff --git a/lib/testelf.c b/lib/testelf.c deleted file mode 100644 index 37861b4d..00000000 --- a/lib/testelf.c +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include "elfparser.h" -#include - -const char *n; - -static void -check (ElfParser *elf, gulong addr) -{ - const ElfSym *sym = elf_parser_lookup_symbol (elf, addr); - - if (!sym) - { - g_print ("not found\n"); - return; - } - - n = elf_parser_get_sym_name (elf, sym); - - g_print ("%p => ", (void *)addr); - - if (sym) - { - g_print ("found: %s (%p)\n", - elf_parser_get_sym_name (elf, sym), - (void *)elf_parser_get_sym_address (elf, sym)); - } - else - { - g_print ("not found\n"); - } -} - -int -main (int argc, char **argv) -{ - ElfParser *elf; - ElfParser *debug = NULL; - const char *build_id; - const char *filename; - const char *dir; - - if (argc == 1) - filename = "/usr/lib/libgtk-x11-2.0.so"; - else - filename = argv[1]; - - elf = elf_parser_new (filename, NULL); - - if (!elf) - { - g_print ("NO ELF!!!!\n"); - return -1; - } - - dir = g_path_get_dirname (filename); - - build_id = elf_parser_get_build_id (elf); - - guint crc = elf_parser_get_crc32 (elf); - - g_print ("build ID: %s crc: %x\n", build_id, crc); - filename = elf_parser_get_debug_link(elf, &crc); - if (filename) - { - filename = g_build_filename ("/usr", "lib", "debug", dir, filename, NULL); - g_print ("Debug link: %s crc: %x\n", filename, crc); - debug = elf_parser_new (filename, NULL); - - if (debug) - { - const char *build = elf_parser_get_build_id (debug); - guint crc_debug = elf_parser_get_crc32 (debug); - g_print ("Debug link build ID: %s crc: %x\n", build, crc_debug); - if (strcmp(build, build_id) != 0 || crc_debug != crc) - g_print ("Build ID or crc not matching!\n"); - } - else - { - g_print ("Separate debug symbol file not found\n"); - } - - } - else - { - g_print ("No debug link\n"); - } - -#if 0 - for (i = 0; i < 5000000; ++i) -#endif - { - elf_parser_get_crc32 (elf); - check (elf, 0x077c80f0 - (0x07787000 - 0)); /* gtk_about_dialog_set_artists (add - (map - offset)) */ - - check (elf, 0x077c80f0 - (0x07787000 - 0)); /* same (but in the middle of the function */ - } - return 0; -} - diff --git a/lib/testunwind.c b/lib/testunwind.c deleted file mode 100644 index c6b40836..00000000 --- a/lib/testunwind.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "elfparser.h" -#include "unwind.h" - -int -main (int argc, char **argv) -{ - const guchar *data; - ElfParser *elf; - - if (argc == 1) - { - g_print ("no arg\n"); - return -1; - } - - elf = elf_parser_new (argv[1], NULL); - - if (!elf) - { - g_print ("NO ELF!!!!\n"); - return -1; - } - - unwind (elf); - - return 0; -} diff --git a/lib/tracker.c b/lib/tracker.c deleted file mode 100644 index 7270543f..00000000 --- a/lib/tracker.c +++ /dev/null @@ -1,1127 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "tracker.h" -#include "stackstash.h" -#include "binfile.h" -#include "elfparser.h" - -typedef struct new_process_t new_process_t; -typedef struct new_map_t new_map_t; -typedef struct sample_t sample_t; -typedef struct fork_t fork_t; -typedef struct exit_t exit_t; - -struct tracker_t -{ - StackStash *stash; - - size_t n_event_bytes; - size_t n_allocated_bytes; - uint8_t * events; -}; - -typedef enum -{ - NEW_PROCESS, - NEW_MAP, - SAMPLE, - FORK, - EXIT -} event_type_t; - -struct new_process_t -{ - uint32_t header; - char command_line[256]; -}; - -struct fork_t -{ - uint32_t header; - int32_t child_pid; -}; - -struct exit_t -{ - uint32_t header; -}; - -struct new_map_t -{ - uint32_t header; - char filename[PATH_MAX]; - uint64_t start; - uint64_t end; - uint64_t offset; - uint64_t inode; -}; - -struct sample_t -{ - uint32_t header; - StackNode * trace; -}; - -#define TYPE_SHIFT 29 -#define PID_MASK ((uint32_t)((1 << TYPE_SHIFT) - 1)) - -#define MAKE_HEADER(type, pid) \ - ((uint32_t)((((uint32_t)pid) & PID_MASK) | (type << TYPE_SHIFT))) - -#define GET_PID(header) \ - (header & PID_MASK) - -#define GET_TYPE(header) \ - (header >> TYPE_SHIFT) - -#define DEFAULT_SIZE (1024 * 1024 * 4) - -static char ** -get_lines (const char *format, pid_t pid) -{ - char *filename = g_strdup_printf (format, pid); - char **result = NULL; - char *contents; - - if (g_file_get_contents (filename, &contents, NULL, NULL)) - { - result = g_strsplit (contents, "\n", -1); - - g_free (contents); - } - - g_free (filename); - - return result; -} - -static void -fake_new_process (tracker_t *tracker, pid_t pid) -{ - char **lines; - gboolean done = FALSE; - - if ((lines = get_lines ("/proc/%d/cmdline", pid))) - { - if (lines[0] && strlen (lines[0]) > 0) - { - tracker_add_process (tracker, pid, lines[0]); - - done = TRUE; - } - - g_strfreev (lines); - } - - if (!done && (lines = get_lines ("/proc/%d/status", pid))) - { - int i; - - for (i = 0; lines[i] != NULL; ++i) - { - if (strncmp ("Name:", lines[i], 5) == 0) - { - char *name = g_strstrip (strchr (lines[i], ':') + 1); - - if (strlen (name) > 0) - { - tracker_add_process (tracker, pid, name); - done = TRUE; - break; - } - } - } - - g_strfreev (lines); - } - - if (!done) - g_print ("failed to fake %d\n", pid); -} - -static void -fake_new_map (tracker_t *tracker, pid_t pid) -{ - char **lines; - - if ((lines = get_lines ("/proc/%d/maps", pid))) - { - int i; - - for (i = 0; lines[i] != NULL; ++i) - { - char file[256]; - gulong start; - gulong end; - gulong offset; - gulong inode; - int count; - - file[255] = '\0'; - - count = sscanf ( - lines[i], "%lx-%lx %*15s %lx %*x:%*x %lu %255s", - &start, &end, &offset, &inode, file); - - if (count == 5) - { - if (strcmp (file, "[vdso]") == 0) - { - /* For the vdso, the kernel reports 'offset' as the - * the same as the mapping addres. This doesn't make - * any sense to me, so we just zero it here. There - * is code in binfile.c (read_inode) that returns 0 - * for [vdso]. - */ - offset = 0; - inode = 0; - } - - tracker_add_map (tracker, pid, start, end, offset, inode, file); - } - } - - g_strfreev (lines); - } -} - -static void -populate_from_proc (tracker_t *tracker) -{ - GDir *proc = g_dir_open ("/proc", 0, NULL); - const char *name; - - if (!proc) - return; - - while ((name = g_dir_read_name (proc))) - { - pid_t pid; - char *end; - - pid = strtol (name, &end, 10); - - if (*end == 0) - { - fake_new_process (tracker, pid); - fake_new_map (tracker, pid); - } - } - - g_dir_close (proc); -} - - -static double -timeval_to_ms (const GTimeVal *timeval) -{ - return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0; -} - -static double -time_diff (const GTimeVal *first, - const GTimeVal *second) -{ - double first_ms = timeval_to_ms (first); - double second_ms = timeval_to_ms (second); - - return first_ms - second_ms; -} - -tracker_t * -tracker_new (void) -{ - tracker_t *tracker = g_new0 (tracker_t, 1); - GTimeVal before, after; - - tracker->n_event_bytes = 0; - tracker->n_allocated_bytes = DEFAULT_SIZE; - tracker->events = g_malloc (DEFAULT_SIZE); - - tracker->stash = stack_stash_new (NULL); - - g_get_current_time (&before); - - populate_from_proc (tracker); - - g_get_current_time (&after); - -#if 0 - g_print ("Time to populate %f\n", time_diff (&after, &before)); -#endif - - return tracker; -} - -void -tracker_free (tracker_t *tracker) -{ - stack_stash_unref (tracker->stash); - g_free (tracker->events); - g_free (tracker); -} - -#define COPY_STRING(dest, src) \ - do \ - { \ - strncpy (dest, src, sizeof (dest) - 1); \ - dest[sizeof (dest) - 1] = 0; \ - } \ - while (0) - - -static void -tracker_append (tracker_t *tracker, - void *event, - int n_bytes) -{ - if (tracker->n_allocated_bytes - tracker->n_event_bytes < n_bytes) - { - size_t new_size = tracker->n_allocated_bytes * 2; - - tracker->events = g_realloc (tracker->events, new_size); - - tracker->n_allocated_bytes = new_size; - } - - g_assert (tracker->n_allocated_bytes - tracker->n_event_bytes >= n_bytes); - - memcpy (tracker->events + tracker->n_event_bytes, event, n_bytes); - -#if 0 - g_print (" (address %p)\n", tracker->events + tracker->n_event_bytes); -#endif - - tracker->n_event_bytes += n_bytes; -} - -void -tracker_add_process (tracker_t * tracker, - pid_t pid, - const char *command_line) -{ - new_process_t event; - -#if 0 - g_print ("Add new process %s %d to tracker ", command_line, pid); -#endif - - event.header = MAKE_HEADER (NEW_PROCESS, pid); - COPY_STRING (event.command_line, command_line); - - tracker_append (tracker, &event, sizeof (event)); -} - -void -tracker_add_fork (tracker_t *tracker, - pid_t pid, - pid_t child_pid) -{ - fork_t event; - - event.header = MAKE_HEADER(FORK, pid); - event.child_pid = child_pid; - - tracker_append (tracker, &event, sizeof (event)); -} - -void -tracker_add_exit (tracker_t *tracker, - pid_t pid) -{ - exit_t event; - - event.header = MAKE_HEADER (EXIT, pid); - - tracker_append (tracker, &event, sizeof (event)); -} - -void -tracker_add_map (tracker_t * tracker, - pid_t pid, - uint64_t start, - uint64_t end, - uint64_t offset, - uint64_t inode, - const char *filename) -{ - new_map_t event; - - event.header = MAKE_HEADER (NEW_MAP, pid); - COPY_STRING (event.filename, filename); - event.start = start; - event.end = end; - event.offset = offset; - event.inode = inode; - - tracker_append (tracker, &event, sizeof (event)); -} - -void -tracker_add_sample (tracker_t *tracker, - pid_t pid, - uint64_t *ips, - int n_ips) -{ - sample_t event; - - event.header = MAKE_HEADER (SAMPLE, pid); - event.trace = stack_stash_add_trace (tracker->stash, ips, n_ips, 1); - - tracker_append (tracker, &event, sizeof (event)); -} - -/* */ -typedef struct state_t state_t; -typedef struct process_t process_t; -typedef struct map_t map_t; - -struct process_t -{ - pid_t pid; - - char * comm; - - GArray * maps; -}; - -struct map_t -{ - char * filename; - uint64_t start; - uint64_t end; - uint64_t offset; - uint64_t inode; -}; - -struct state_t -{ - GHashTable *processes_by_pid; - GHashTable *unique_comms; - GHashTable *unique_symbols; - GHashTable *bin_files; -}; - -static const map_t * -process_locate_map (process_t *process, gulong addr) -{ - GArray *maps = process->maps; - int i; - - for (i = 0; i < process->maps->len; ++i) - { - map_t *map = &g_array_index (maps, map_t, i); - - if (addr >= map->start && addr < map->end) - { - if (i > 0) - { - map_t tmp = *map; - - memmove (&(g_array_index (maps, map_t, 1)), - &(g_array_index (maps, map_t, 0)), - i * sizeof (map_t)); - - g_array_index (maps, map_t, 0) = tmp; - } - - return &g_array_index (maps, map_t, 0); - } - } - - return NULL; -} - -static void -create_map (state_t *state, new_map_t *new_map) -{ - process_t *process; - map_t map; - int i; - pid_t pid = GET_PID (new_map->header); - - process = g_hash_table_lookup ( - state->processes_by_pid, GINT_TO_POINTER (pid)); - - if (!process) - return; - - map.filename = g_strdup (new_map->filename); - map.start = new_map->start; - map.end = new_map->end; - map.offset = new_map->offset; - map.inode = new_map->inode; - - /* Remove existing maps that overlap the new one */ - for (i = 0; i < process->maps->len; ++i) - { - map_t *m = &g_array_index (process->maps, map_t, i); - - if (m->start < map.end && m->end > map.start) - { - g_free (m->filename); - - g_array_remove_index (process->maps, i); - } - } - - g_array_append_vals (process->maps, &map, 1); -} - -static void -destroy_process (process_t *process) -{ - int i; - - g_free (process->comm); - - for (i = 0; i < process->maps->len; ++i) - { - map_t *map = &g_array_index (process->maps, map_t, i); - - g_free (map->filename); - } - - g_array_free (process->maps, TRUE); - g_free (process); -} - -static void -create_process (state_t *state, new_process_t *new_process) -{ - pid_t pid = GET_PID (new_process->header); - const char *comm = new_process->command_line; - - process_t *process = - g_hash_table_lookup (state->processes_by_pid, GINT_TO_POINTER (pid)); - - if (process) - { - g_free (process->comm); - process->comm = g_strdup (comm); - } - else - { - process = g_new0 (process_t, 1); - - process->pid = pid; - process->comm = g_strdup (comm); - process->maps = g_array_new (FALSE, FALSE, sizeof (map_t)); - - g_hash_table_insert ( - state->processes_by_pid, GINT_TO_POINTER (process->pid), process); - } -} - -static void -process_fork (state_t *state, fork_t *fork) -{ - pid_t ppid = GET_PID (fork->header); - - process_t *parent = g_hash_table_lookup ( - state->processes_by_pid, GINT_TO_POINTER (ppid)); - process_t *child; - - if (ppid == fork->child_pid) - { - /* Just a new thread being spawned */ - return; - } - - child = g_new0 (process_t, 1); - if (parent) - child->comm = g_strdup (parent->comm); - else - child->comm = g_strdup_printf ("[pid %d]", fork->child_pid); - - child->pid = fork->child_pid; - - child->maps = g_array_new (FALSE, FALSE, sizeof (map_t)); - - if (parent) - { - int i; - - for (i = 0; i < parent->maps->len; ++i) - { - map_t copy = g_array_index (parent->maps, map_t, i); - - copy.filename = g_strdup (copy.filename); - - g_array_append_val (child->maps, copy); - } - } - - g_hash_table_insert ( - state->processes_by_pid, GINT_TO_POINTER (child->pid), child); -} - -static void -process_exit (state_t *state, exit_t *exit) -{ -#if 0 - g_print ("Exit for %d\n", exit->pid); -#endif - - /* ignore for now */ -} - -static void -free_process (gpointer data) -{ - process_t *process = data; - - destroy_process (process); -} - -static state_t * -state_new (void) -{ - state_t *state = g_new0 (state_t, 1); - - state->processes_by_pid = - g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, free_process); - - state->unique_symbols = g_hash_table_new (g_direct_hash, g_direct_equal); - state->unique_comms = g_hash_table_new (g_str_hash, g_str_equal); - state->bin_files = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, - (GDestroyNotify)bin_file_free); - - return state; -} - -static void -state_free (state_t *state) -{ - g_hash_table_destroy (state->processes_by_pid); - g_hash_table_destroy (state->unique_symbols); - g_hash_table_destroy (state->unique_comms); - g_hash_table_destroy (state->bin_files); - - g_free (state); -} - -typedef struct -{ - gulong address; - char *name; -} kernel_symbol_t; - -static void -parse_kallsym_line (const char *line, GArray *table) -{ - char **tokens = g_strsplit_set (line, " \t", -1); - - if (tokens[0] && tokens[1] && tokens[2]) - { - glong address; - char *endptr; - - address = strtoul (tokens[0], &endptr, 16); - - if (*endptr == '\0' && - (strcmp (tokens[1], "T") == 0 || - strcmp (tokens[1], "t") == 0)) - { - kernel_symbol_t sym; - - sym.address = address; - sym.name = g_strdup (tokens[2]); - - g_array_append_val (table, sym); - } - } - - g_strfreev (tokens); -} - -static gboolean -parse_kallsyms (const char *kallsyms, - GArray *table) -{ - const char *sol; - const char *eol; - - sol = kallsyms; - eol = strchr (sol, '\n'); - while (eol) - { - char *line = g_strndup (sol, eol - sol); - - parse_kallsym_line (line, table); - - g_free (line); - - sol = eol + 1; - eol = strchr (sol, '\n'); - } - - if (table->len <= 1) - return FALSE; - - return TRUE; -} - -static int -compare_syms (gconstpointer a, gconstpointer b) -{ - const kernel_symbol_t *sym_a = a; - const kernel_symbol_t *sym_b = b; - - if (sym_a->address > sym_b->address) - return 1; - else if (sym_a->address == sym_b->address) - return 0; - else - return -1; -} - -static kernel_symbol_t * -do_lookup (kernel_symbol_t *symbols, - gulong address, - int first, - int last) -{ - if (address >= symbols[last].address) - { - return &(symbols[last]); - } - else if (last - first < 3) - { - while (last >= first) - { - if (address >= symbols[last].address) - return &(symbols[last]); - - last--; - } - - return NULL; - } - else - { - int mid = (first + last) / 2; - - if (symbols[mid].address > address) - return do_lookup (symbols, address, first, mid); - else - return do_lookup (symbols, address, mid, last); - } -} - -static GArray * -get_kernel_symbols (void) -{ - static GArray *kernel_syms; - static gboolean initialized = FALSE; - -#if 0 - find_kernel_binary(); -#endif - - if (!initialized) - { - char *kallsyms; - if (g_file_get_contents ("/proc/kallsyms", &kallsyms, NULL, NULL)) - { - if (kallsyms) - { - kernel_syms = g_array_new (TRUE, TRUE, sizeof (kernel_symbol_t)); - - if (parse_kallsyms (kallsyms, kernel_syms)) - { - g_array_sort (kernel_syms, compare_syms); - } - else - { - g_array_free (kernel_syms, TRUE); - kernel_syms = NULL; - } - } - - g_free (kallsyms); - } - - if (!kernel_syms) - { - g_print ("Warning: /proc/kallsyms could not be " - "read. Kernel symbols will not be available\n"); - } - - initialized = TRUE; - } - - return kernel_syms; -} - -static const char skip_kernel_symbols[][32] = -{ - /* IRQ stack */ - "common_interrupt", - "apic_timer_interrupt", - "smp_apic_timer_interrupt", - "hrtimer_interrupt", - "__run_hrtimer", - "perf_swevent_hrtimer", - "perf_event_overflow", - "__perf_event_overflow", - "perf_prepare_sample", - "perf_callchain", - "perf_swcounter_hrtimer", - "perf_counter_overflow", - "__perf_counter_overflow", - "perf_counter_output", - - /* NMI stack */ - "nmi_stack_correct", - "do_nmi", - "notify_die", - "atomic_notifier_call_chain", - "notifier_call_chain", - "perf_event_nmi_handler", - "perf_counter_nmi_handler", - "intel_pmu_handle_irq", - "perf_event_overflow", - "perf_counter_overflow", - "__perf_event_overflow", - "perf_prepare_sample", - "perf_callchain", - - "" -}; - -const char * -lookup_kernel_symbol (gulong address) -{ - kernel_symbol_t *result; - GArray *ksyms = get_kernel_symbols (); - const char *sym; - int i; - - if (ksyms->len == 0) - return NULL; - - result = do_lookup ((kernel_symbol_t *)ksyms->data, - address, 0, ksyms->len - 1); - - sym = result? result->name : NULL; - - - /* This is a workaround for a kernel bug, where it reports not - * only the kernel stack, but also the IRQ stack for the - * timer interrupt that generated the stack. - * - * The stack as reported by the kernel looks like this: - * - * [ip] [irq stack] [real kernel stack] - * - * Below we filter out the [irq stack] - */ - i = 0; - while (sym && skip_kernel_symbols[i][0] != '\0') - { - if (strcmp (sym, skip_kernel_symbols[i]) == 0) - { - sym = NULL; - break; - } - i++; - } - - return sym; -} - -/* Note that 'unique_symbols' is a direct_hash table. Ie., we - * rely on the address of symbol strings being different for different - * symbols. - */ -static char * -unique_dup (GHashTable *unique_symbols, const char *sym) -{ - char *result; - - result = g_hash_table_lookup (unique_symbols, sym); - if (!result) - { - result = elf_demangle (sym); - g_hash_table_insert (unique_symbols, (char *)sym, result); - } - - return result; -} - -static const char * -make_message (state_t *state, const char *format, ...) -{ - va_list args; - char *message; - char *result; - - va_start (args, format); - g_vasprintf (&message, format, args); - va_end (args); - - result = g_hash_table_lookup (state->unique_comms, message); - if (result) - { - g_free (message); - } - else - { - result = message; - - g_hash_table_insert (state->unique_comms, result, result); - } - - return result; -} - -static bin_file_t * -state_get_bin_file (state_t *state, const char *filename) -{ - bin_file_t *bf = g_hash_table_lookup (state->bin_files, filename); - - if (!bf) - { - bf = bin_file_new (filename); - - g_hash_table_insert (state->bin_files, g_strdup (filename), bf); - } - - return bf; -} - -static char * -lookup_symbol (state_t *state, - process_t *process, - uint64_t address, - gboolean kernel) -{ - const char *sym; - - g_assert (process); - - if (kernel) - { - sym = lookup_kernel_symbol (address); - } - else - { - const map_t *map = process_locate_map (process, address); - - if (!map) - { - sym = make_message (state, "No map [%s]", process->comm); - } - else - { - bin_file_t *bin_file = state_get_bin_file (state, map->filename); - const bin_symbol_t *bin_sym; - - address -= map->start; - address += map->offset; - - if (map->inode && !bin_file_check_inode (bin_file, map->inode)) - { - /* If the inodes don't match, it's probably because the - * file has changed since the process was started. - */ - sym = make_message (state, "%s: inode mismatch", map->filename); - } - else - { - bin_sym = bin_file_lookup_symbol (bin_file, address); - - sym = bin_symbol_get_name (bin_file, bin_sym); - } - } - } - - if (sym) - return unique_dup (state->unique_symbols, sym); - else - return NULL; -} - -typedef struct context_info_t context_info_t; -struct context_info_t -{ - enum perf_callchain_context context; - char name[32]; -}; - -static const context_info_t context_info[] = -{ - { PERF_CONTEXT_HV, "- - hypervisor - -" }, - { PERF_CONTEXT_KERNEL, "- - kernel - -" }, - { PERF_CONTEXT_USER, "- - user - - " }, - { PERF_CONTEXT_GUEST, "- - guest - - " }, - { PERF_CONTEXT_GUEST_KERNEL, "- - guest kernel - -" }, - { PERF_CONTEXT_GUEST_USER, "- - guest user - -" }, -}; - -static const char *const everything = "[Everything]"; - -static const context_info_t * -get_context_info (enum perf_callchain_context context) -{ - int i; - - for (i = 0; i < sizeof (context_info) / sizeof (context_info[0]); ++i) - { - const context_info_t *info = &context_info[i]; - - if (info->context == context) - return info; - } - - return NULL; -} - -static void -process_sample (state_t *state, StackStash *resolved, sample_t *sample) -{ - const context_info_t *context = NULL; - const char *cmdline; - uint64_t stack_addrs[256]; - uint64_t *resolved_traces; - process_t *process; - StackNode *n; - int len; - pid_t pid = GET_PID (sample->header); - - process = g_hash_table_lookup ( - state->processes_by_pid, GINT_TO_POINTER (pid)); - - if (!process) - { - static gboolean warned; - if (!warned || pid != 0) - { -#if 0 - g_print ("sample for unknown process %d\n", sample->pid); -#endif - warned = TRUE; - } - return; - } - - len = 5; - for (n = sample->trace; n != NULL; n = n->parent) - len++; - - if (len > 256) - resolved_traces = g_new (uint64_t, len); - else - resolved_traces = stack_addrs; - - len = 0; - for (n = sample->trace; n != NULL; n = n->parent) - { - uint64_t address = n->data; - const context_info_t *new_context; - const char *symbol; - - new_context = get_context_info (address); - if (new_context) - { - if (context) - symbol = unique_dup (state->unique_symbols, context->name); - else - symbol = NULL; - - context = new_context; - } - else - { - gboolean kernel = context && context->context == PERF_CONTEXT_KERNEL; - - symbol = lookup_symbol (state, process, address, kernel); - } - - if (symbol) - resolved_traces[len++] = POINTER_TO_U64 (symbol); - } - - if (context && context->context != PERF_CONTEXT_USER) - { - /* Kernel threads do not have a user part, so we end up here - * without ever getting a user context. If this happens, - * add the '- - kernel - - ' name, so that kernel threads - * are properly blamed on the kernel - */ - resolved_traces[len++] = - POINTER_TO_U64 (unique_dup (state->unique_symbols, context->name)); - } - - cmdline = make_message (state, "[%s]", process->comm); - - resolved_traces[len++] = POINTER_TO_U64 (cmdline); - resolved_traces[len++] = POINTER_TO_U64 ( - unique_dup (state->unique_symbols, everything)); - - stack_stash_add_trace (resolved, resolved_traces, len, 1); - - if (resolved_traces != stack_addrs) - g_free (resolved_traces); -} - -Profile * -tracker_create_profile (tracker_t *tracker) -{ - uint8_t *end = tracker->events + tracker->n_event_bytes; - StackStash *resolved_stash; - Profile *profile; - state_t *state; - uint8_t *event; - - state = state_new (); - resolved_stash = stack_stash_new (g_free); - - event = tracker->events; - while (event < end) - { - event_type_t type = GET_TYPE (*(uint32_t *)event); - - switch (type) - { - case NEW_PROCESS: - create_process (state, (new_process_t *)event); - event += sizeof (new_process_t); - break; - - case NEW_MAP: - create_map (state, (new_map_t *)event); - event += sizeof (new_map_t); - break; - - case FORK: - process_fork (state, (fork_t *)event); - event += sizeof (fork_t); - break; - - case EXIT: - process_exit (state, (exit_t *)event); - event += sizeof (exit_t); - break; - - case SAMPLE: - process_sample (state, resolved_stash, (sample_t *)event); - event += sizeof (sample_t); - break; - } - } - - profile = profile_new (resolved_stash); - - state_free (state); - stack_stash_unref (resolved_stash); - - return profile; -} diff --git a/lib/tracker.h b/lib/tracker.h deleted file mode 100644 index 098f4a17..00000000 --- a/lib/tracker.h +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include "profile.h" - -typedef struct tracker_t tracker_t; - -tracker_t *tracker_new (void); -void tracker_free (tracker_t *); - -void tracker_add_process (tracker_t *tracker, - pid_t pid, - const char *command_line); -void tracker_add_fork (tracker_t *tracker, - pid_t pid, - pid_t child_pid); -void tracker_add_exit (tracker_t *tracker, - pid_t pid); -void tracker_add_map (tracker_t * tracker, - pid_t pid, - uint64_t start, - uint64_t end, - uint64_t offset, - uint64_t inode, - const char *filename); -void tracker_add_sample (tracker_t *tracker, - pid_t pid, - uint64_t *ips, - int n_ips); - -Profile *tracker_create_profile (tracker_t *tracker); diff --git a/lib/treeviewutils.c b/lib/treeviewutils.c deleted file mode 100644 index 20d7777f..00000000 --- a/lib/treeviewutils.c +++ /dev/null @@ -1,362 +0,0 @@ -/* MemProf -- memory profiler and leak detector - * Copyright 2002, Soeren Sandmann (sandmann@daimi.au.dk) - * Copyright 2003, 2004, Red Hat, Inc. - * - * Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, 2005, 2006, 2007, 2008 Soeren Sandmann - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -/*====*/ - -#include "treeviewutils.h" - -static void on_column_clicked (GtkTreeViewColumn *column, - gpointer data); -typedef struct -{ - int model_column; - GtkSortType default_order; -} SortInfo; - -static void -set_sort_info (GtkTreeView *view, - GtkTreeViewColumn *column, - int model_column, - GtkSortType default_order) -{ - SortInfo *info; - - info = g_new0 (SortInfo, 1); - info->model_column = model_column; - info->default_order = default_order; - - gtk_tree_view_column_set_clickable (column, TRUE); - - g_object_set_data (G_OBJECT (column), "column_info", info); - g_signal_connect (column, "clicked", G_CALLBACK (on_column_clicked), view); -} - -static SortInfo * -get_sort_info (GtkTreeViewColumn *column) -{ - return g_object_get_data (G_OBJECT (column), "column_info"); -} - -static GtkTreeViewColumn * -find_column_by_model_column (GtkTreeView *view, - int model_column) -{ - GList *columns = gtk_tree_view_get_columns (view); - GList *list; - GtkTreeViewColumn *result = NULL; - - for (list = columns; list != NULL; list = list->next) - { - GtkTreeViewColumn *column = list->data; - SortInfo *info = get_sort_info (column); - - if (info->model_column == model_column) - result = column; - } - - g_list_free (columns); - - return result; -} - -void -tree_view_set_sort_column (GtkTreeView *view, - int model_column, - int sort_type) -{ - GList *columns, *list; - GtkTreeSortable *sortable; - GtkTreeViewColumn *column = find_column_by_model_column (view, model_column); - SortInfo *info = get_sort_info (column); - - /* For each column in the view, unset the sort indicator */ - columns = gtk_tree_view_get_columns (view); - for (list = columns; list != NULL; list = list->next) - { - GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN (list->data); - - gtk_tree_view_column_set_sort_indicator (col, FALSE); - } - - /* Set the sort indicator for this column */ - gtk_tree_view_column_set_sort_indicator (column, TRUE); - gtk_tree_view_column_set_sort_order (column, sort_type); - - /* And sort the column */ - sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (view)); - - gtk_tree_sortable_set_sort_column_id (sortable, - info->model_column, - sort_type); -} - -static void -on_column_clicked (GtkTreeViewColumn *column, - gpointer data) -{ - GtkTreeView *view = data; - GtkSortType sort_type; - SortInfo *info = get_sort_info (column); - - /* Find out what our current sort indicator is. If it is NONE, then - * select the default for the column, otherwise, select the opposite - */ - if (!gtk_tree_view_column_get_sort_indicator (column)) - { - sort_type = info->default_order; - } - else - { - if (gtk_tree_view_column_get_sort_order (column) == GTK_SORT_ASCENDING) - sort_type = GTK_SORT_DESCENDING; - else - sort_type = GTK_SORT_ASCENDING; - } - - tree_view_set_sort_column (view, info->model_column, sort_type); -} - -GtkTreeViewColumn * -add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column) -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - - renderer = gtk_cell_renderer_text_new (); - g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); - column = gtk_tree_view_column_new_with_attributes (title, renderer, - "text", model_column, - NULL); - gtk_tree_view_column_set_resizable (column, TRUE); - gtk_tree_view_append_column (view, column); - - set_sort_info (view, column, model_column, GTK_SORT_ASCENDING); - - return column; -} - -static void -pointer_to_text (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, GtkTreeModel *tree_model, - GtkTreeIter *iter, gpointer data) -{ - gpointer p; - gchar *text; - int column = GPOINTER_TO_INT (data); - - gtk_tree_model_get (tree_model, iter, column, &p, -1); - text = g_strdup_printf ("%p", p); - g_object_set (cell, "text", text, NULL); - g_free (text); -} - -typedef struct { - int column; - char *format; -} ColumnInfo; - -static void -double_to_text (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, GtkTreeModel *tree_model, - GtkTreeIter *iter, gpointer data) -{ - gdouble d; - gchar *text; - ColumnInfo *info = data; - - gtk_tree_model_get (tree_model, iter, info->column, &d, -1); - - text = g_strdup_printf (info->format, d); - - g_object_set (cell, "markup", text, NULL); - g_free (text); -} - -static void -free_column_info (void *data) -{ - ColumnInfo *info = data; - g_free (info->format); - g_free (info); -} - -GtkTreeViewColumn * -add_double_format_column (GtkTreeView *view, - const gchar *title, - gint model_column, - const char *format) -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - ColumnInfo *column_info = g_new (ColumnInfo, 1); - - renderer = gtk_cell_renderer_text_new (); - g_object_set (renderer, "xalign", 1.0, NULL); - - column = gtk_tree_view_column_new (); - gtk_tree_view_column_set_title (column, title); - gtk_tree_view_column_pack_start (column, renderer, TRUE); - gtk_tree_view_column_set_resizable (column, FALSE); - - column_info->column = model_column; - column_info->format = g_strdup (format); - - gtk_tree_view_column_set_cell_data_func ( - column, renderer, double_to_text, column_info, free_column_info); - - gtk_tree_view_append_column (view, column); - - set_sort_info (view, column, model_column, GTK_SORT_DESCENDING); - - return column; -} - -GtkTreeViewColumn * -add_pointer_column (GtkTreeView *view, const gchar *title, gint model_column) -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - - renderer = gtk_cell_renderer_text_new (); - - column = gtk_tree_view_column_new (); - if (title) - gtk_tree_view_column_set_title (column, title); - gtk_tree_view_column_pack_start (column, renderer, TRUE); - gtk_tree_view_column_set_resizable (column, TRUE); - gtk_tree_view_column_set_cell_data_func ( - column, renderer, pointer_to_text, GINT_TO_POINTER (model_column), NULL); - - gtk_tree_view_append_column (view, column); - - return column; -} - -void -tree_view_set_model_with_default_sort (GtkTreeView *view, - GtkTreeModel *model, - int model_column, - GtkSortType default_sort) -{ - int column; - GtkSortType type; - GtkTreeSortable *old_model; - GtkAdjustment *adjustment; - - old_model = GTK_TREE_SORTABLE (gtk_tree_view_get_model (view)); - - if (!(old_model && gtk_tree_sortable_get_sort_column_id ( - GTK_TREE_SORTABLE (old_model), &column, &type))) - { - column = model_column; - type = default_sort; - } - - /* Setting the sort column here prevents the "rows_reordered" - * signal from being emitted when the model is attached to - * a treeview. This is desirable because at that point there - * is a handler attached, which means the signal will actually - * be emitted. - */ - gtk_tree_sortable_set_sort_column_id ( - GTK_TREE_SORTABLE (model), column, type); - - gtk_tree_view_set_model (view, model); - - tree_view_set_sort_column (view, column, type); - - /* Workaround for GTK+ crack, see bug 405625 */ - adjustment = gtk_tree_view_get_vadjustment (view); - if (adjustment) - gtk_adjustment_set_value (adjustment, 0); -} - -static void -process_iter (GtkTreeView *view, - GtkTreeIter *iter, - VisibleCallback callback, - gpointer data) -{ - GtkTreeModel *model = gtk_tree_view_get_model (view); - GtkTreePath *path; - GtkTreeIter child; - - path = gtk_tree_model_get_path (model, iter); - - callback (view, path, iter, data); - - if (gtk_tree_view_row_expanded (view, path)) - { - if (gtk_tree_model_iter_children (model, &child, iter)) - { - do - { - process_iter (view, &child, callback, data); - } - while (gtk_tree_model_iter_next (model, &child)); - } - } - - gtk_tree_path_free (path); -} - -void -tree_view_foreach_visible (GtkTreeView *view, - VisibleCallback callback, - gpointer data) -{ - GtkTreeModel *model = gtk_tree_view_get_model (view); - GtkTreeIter iter; - - if (model && gtk_tree_model_get_iter_first (model, &iter)) - process_iter (view, &iter, callback, data); -} - -/* Not really a *treeview* utility, but there isn't really a - * better place to put it - */ -void -set_error (GError **err, gint domain, gint code, - const char *format, va_list args) -{ - char *msg; - - if (!err) - return; - - msg = g_strdup_vprintf (format, args); - - if (*err == NULL) - { - *err = g_error_new_literal (G_MARKUP_ERROR, code, msg); - } - else - { - /* Warning text from GLib */ - g_warning ("GError set over the top of a previous GError or uninitialized memory.\n" - "This indicates a bug in someone's code. You must ensure an error is NULL before it's set.\n" - "The overwriting error message was: %s", - msg); - } - - g_free (msg); -} - diff --git a/lib/treeviewutils.h b/lib/treeviewutils.h deleted file mode 100644 index a6eb1e5e..00000000 --- a/lib/treeviewutils.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- mode: C; c-file-style: "linux" -*- */ - -/* MemProf -- memory profiler and leak detector - * Copyright 2002, Soeren Sandmann (sandmann@daimi.au.dk) - * Copyright 2003, 2004, Red Hat, 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 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -/*====*/ - -#include - -GtkTreeViewColumn *add_plain_text_column (GtkTreeView *view, - const char *title, - gint model_column); -GtkTreeViewColumn *add_double_format_column (GtkTreeView *view, - const char *title, - int model_column, - const char *format); -GtkTreeViewColumn *add_pointer_column (GtkTreeView *view, - const char *title, - int model_column); -void tree_view_set_model_with_default_sort (GtkTreeView *view, - GtkTreeModel *model, - int model_column, - GtkSortType default_sort); -void tree_view_set_sort_column (GtkTreeView *view, - int model_column, - int sort_type); - -typedef void (* VisibleCallback) (GtkTreeView *view, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data); -void tree_view_foreach_visible (GtkTreeView *view, - VisibleCallback callback, - gpointer data); - - - -void -set_error_va (GError **err, gint domain, gint code, - const char *format, va_list args); diff --git a/lib/unwind.c b/lib/unwind.c deleted file mode 100644 index d7c23e18..00000000 --- a/lib/unwind.c +++ /dev/null @@ -1,399 +0,0 @@ -#include "elfparser.h" -#include -#include "util.h" - -/* Pointer encodings, from dwarf2.h. */ -typedef enum -{ - DW_EH_PE_absptr = 0x00, /* */ - DW_EH_PE_omit = 0xff, /* Value is not there */ - - DW_EH_PE_uleb128 = 0x01, - DW_EH_PE_udata2 = 0x02, - DW_EH_PE_udata4 = 0x03, - DW_EH_PE_udata8 = 0x04, - DW_EH_PE_sleb128 = 0x09, - DW_EH_PE_sdata2 = 0x0A, - DW_EH_PE_sdata4 = 0x0B, - DW_EH_PE_sdata8 = 0x0C, - DW_EH_PE_signed = 0x08, - - DW_EH_PE_pcrel = 0x10, /* Value is *(cur + val) */ - DW_EH_PE_textrel = 0x20, /* Value is *(&text + val) */ - DW_EH_PE_datarel = 0x30, /* Value is *(&data + val) */ - DW_EH_PE_funcrel = 0x40, /* Value is *(fde.pc_begin + val) */ - DW_EH_PE_aligned = 0x50, /* Value is absolute, and stored - * at next align */ - - DW_EH_PE_indirect = 0x80 -} PointerEncoding; - -typedef struct EncodedPointer EncodedPointer; -struct EncodedPointer -{ - PointerEncoding encoding; - guint64 value; -}; - -static guint64 -get_length (const guchar **data) -{ - guint64 len; - - len = *(guint32 *)*data; - - *data += 4; - - if (len == 0xffffffff) - { - len = *(guint64 *)data; - *data += 8; - } - - return len; -} - -static guint64 -decode_uleb128 (const guchar **data) -{ - guint64 result; - int shift; - guchar b; - - result = 0; - shift = 0; - do - { - b = *(*data)++; - result |= (b & 0x7f) << shift; - shift += 7; - - } while (b & 0x80); - - return result; -} - -static gint64 -decode_sleb128 (const guchar **data) -{ - gint64 result; - int shift; - guchar b; - - result = 0; - shift = 0; - do - { - b = *(*data)++; - result |= (b & 0x7f) << shift; - shift += 7; - } while (b & 0x80); - - if (b & 0x40 && shift < 64) - result |= - (1 << shift); - - return result; -} - -static void -decode_block (const guchar **data) -{ - int len; - - /* FIXME */ - - len = decode_uleb128 (data); - - (*data) += len; -} - -static gulong -decode_address (const guchar **data) -{ - /* FIXME */ - gulong r; - - r = *(guint32 *)*data; - (*data) += 4; - return r; -} - -static const char * -decode_instruction (const guchar **data) -{ - int opcode = *(*data)++; - int high2 = (opcode & 0xc0) >> 6; - int low6 = (opcode & 0x3f); - - if (high2 == 0x01) - { - return "DW_CFA_advance_loc"; - } - else if (high2 == 0x02) - { - g_print ("register: %d\n", low6); - g_print ("offset: "FMT64"\n", decode_uleb128 (data)); - - return "DW_CFA_offset"; - } - else if (high2 == 0x03) - { - return "DW_CFA_restore"; - } - else - { - g_assert ((opcode & 0xc0) == 0); - - switch (opcode) - { - case 0x0: - return "DW_CFA_nop"; - - case 0x01: - g_print ("addr: %p\n", (void *)decode_address (data)); - return "DW_CFA_set_loc"; - - case 0x02: - (*data)++; - return "DW_CFA_advance_loc1"; - - case 0x03: - (*data) += 2; - return "DW_CFA_advance_loc2"; - - case 0x04: - (*data) += 4; - return "DW_CFA_advance_loc4"; - - case 0x05: - decode_uleb128 (data); - decode_uleb128 (data); - return "DW_CFA_offset_extended"; - - case 0x06: - decode_uleb128 (data); - return "DW_CFA_restore_extended"; - - case 0x07: - decode_uleb128 (data); - return "DW_CFA_undefined"; - - case 0x08: - decode_uleb128 (data); - return "DW_CFA_same_value"; - - case 0x09: - decode_uleb128 (data); - decode_uleb128 (data); - return "DW_CFA_register"; - - case 0x0a: - return "DW_CFA_remember_state"; - - case 0x0b: - return "DW_CFA_restore_state"; - - case 0x0c: - g_print ("reg: "FMT64"\n", decode_uleb128 (data)); - g_print ("off: "FMT64"\n", decode_uleb128 (data)); - return "DW_CFA_def_cfa"; - - case 0x0d: - decode_uleb128 (data); - return "DW_CFA_def_cfa_register"; - - case 0x0e: - decode_uleb128 (data); - return "DW_CFA_def_cfa_offset"; - - case 0x0f: - decode_block (data); - return "DW_CFA_def_cfa_expression"; - - case 0x10: - decode_uleb128 (data); - decode_block (data); - return "DW_CFA_expression"; - - case 0x11: - decode_uleb128 (data); - decode_sleb128 (data); - return "DW_CFA_offset_extended_sf"; - - case 0x12: - decode_uleb128 (data); - decode_sleb128 (data); - return "DW_CFA_def_cfa_sf"; - - case 0x13: - decode_sleb128 (data); - return "DW_CFA_def_cfa_offset_sf"; - - case 0x14: - decode_uleb128 (data); - decode_uleb128 (data); - return "DW_CFA_val_offset"; - - case 0x15: - decode_uleb128 (data); - decode_sleb128 (data); - return "DW_CFA_val_offset_sf"; - - case 0x16: - decode_uleb128 (data); - decode_block (data); - return "DW_CFA_val_expression"; - - case 0x1c: - return "DW_CFA_lo_user"; - - case 0x3f: - return "DW_CFA_hi_user"; - - default: - return "UNKNOWN INSTRUCTION"; - } - } -} - -typedef struct CIE CIE; -struct CIE -{ - PointerEncoding encoding; -}; - -static void -decode_cie (const guchar **data, const guchar *end) -{ - gboolean has_augmentation; - guint64 aug_len; - const char *augmentation; - CIE *cie; - int i, field; - - g_print ("version: %d\n", *(*data)++); - - augmentation = (*data); - - *data += strlen (*data) + 1; - - g_print ("code alignment: "FMT64"\n", decode_uleb128 (data)); - - g_print ("data alignment: %lld\n", decode_sleb128 (data)); - - g_print ("return register: "FMT64"\n", decode_uleb128 (data)); - - g_print ("augmentation: %s\n", augmentation); - - if (augmentation[0] == 'z') - { - aug_len = decode_uleb128 (data); - - g_print ("len: "FMT64"\n", aug_len); - - for (i = 1; augmentation[i] != 0; ++i) - { - if (augmentation[i] == 'L') - { - - } - } - } - - - if (has_augmentation) - { - g_print ("%x\n", **data); - - *data += aug_len; - } - - while (*data < end) - g_print (" %s\n", decode_instruction (data)); -} - -static gboolean -decode_fde (const guchar **data, const guchar *end) -{ - - - return FALSE; -} - -static gboolean -decode_entry (const guchar **data, gboolean eh_frame) -{ - guint64 len; - const guchar *end; - gboolean is_cie; - guint64 id; - - len = get_length (data); - - if (len == 0) - return FALSE; - - end = *data + len; - - g_print ("length: "FMT64"\n", len); - - /* CIE_id is 0 for eh frames, and 0xffffffff/0xffffffffffffffff for .debug_frame */ - - id = *(guint32 *)*data; - - g_print ("id: %lld\n", id); - - is_cie = (eh_frame && id == 0) || (!eh_frame && id == 0xffffffff); - - if (is_cie) - g_print ("is cie\n"); - else - g_print ("is not cie\n"); - - *data += 4; - - if (is_cie) - decode_cie (data, end); - else - decode_fde (data, end); - - return TRUE; -} - -/* The correct API is probably something like - * - * gboolean - * unwind (ElfParser *parser, - * gulong *regs - * int n_regs, - * MemoryReader reader, - * gpointer data); - * - */ -void -unwind (ElfParser *elf) -{ - const guchar *data; - gboolean eh_f; - - if ((data = elf_parser_get_debug_frame (elf))) - { - g_print ("Using .debug_frame\n"); - eh_f = FALSE; - } - else if ((data = elf_parser_get_eh_frame (elf))) - { - g_print ("Using .eh_frame\n"); - eh_f = TRUE; - } - else - { - g_print ("no debug info found\n"); - return; - } - - while (decode_entry (&data, eh_f)) - return ; - ; -} - diff --git a/lib/unwind.h b/lib/unwind.h deleted file mode 100644 index 2827e742..00000000 --- a/lib/unwind.h +++ /dev/null @@ -1,3 +0,0 @@ -#include - -void unwind (const guchar *data); diff --git a/lib/util.h b/lib/util.h deleted file mode 100644 index c1fb1dfd..00000000 --- a/lib/util.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef UTIL_H -#define UTIL_H - -#define FMT64 "%"G_GUINT64_FORMAT - -#if defined(__i386__) -#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") -#define cpu_relax() asm volatile("rep; nop" ::: "memory"); -#endif - -#if defined(__x86_64__) -#define rmb() asm volatile("lfence" ::: "memory") -#define cpu_relax() asm volatile("rep; nop" ::: "memory"); -#endif - -#ifdef __powerpc__ -#define rmb() asm volatile ("sync" ::: "memory") -#define cpu_relax() asm volatile ("" ::: "memory"); -#endif - -#ifdef __s390__ -#define rmb() asm volatile("bcr 15,0" ::: "memory") -#define cpu_relax() asm volatile("" ::: "memory"); -#endif - -#ifdef __sh__ -#if defined(__SH4A__) || defined(__SH5__) -# define rmb() asm volatile("synco" ::: "memory") -#else -# define rmb() asm volatile("" ::: "memory") -#endif -#define cpu_relax() asm volatile("" ::: "memory") -#endif - -#ifdef __hppa__ -#define rmb() asm volatile("" ::: "memory") -#define cpu_relax() asm volatile("" ::: "memory"); -#endif - -#endif diff --git a/lib/util/binfile.c b/lib/util/binfile.c new file mode 100644 index 00000000..be181c3b --- /dev/null +++ b/lib/util/binfile.c @@ -0,0 +1,540 @@ +/* MemProf -- memory profiler and leak detector + * Copyright 1999, 2000, 2001, Red Hat, Inc. + * Copyright 2002, Kristian Rietveld + * + * Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, 2005, 2006, 2007, Soeren Sandmann + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Most interesting code in this file is lifted from bfdutils.c + * and process.c from Memprof, + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "binfile.h" +#include "elfparser.h" +#include "util.h" + +struct bin_file_t +{ + int ref_count; + + GList * elf_files; + + char * filename; + + char * undefined_name; + + gulong text_offset; + + gboolean inode_check; + ino_t inode; +}; + +static ino_t +read_inode (const char *filename) +{ + struct stat statbuf; + + if (strcmp (filename, "[vdso]") == 0) + return (ino_t)0; + + if (stat (filename, &statbuf) < 0) + return (ino_t)-1; + + return statbuf.st_ino; +} + +static gboolean +already_warned (const char *name) +{ + static GPtrArray *warnings; + guint i; + + if (!warnings) + warnings = g_ptr_array_new (); + + for (i = 0; i < warnings->len; ++i) + { + if (strcmp (warnings->pdata[i], name) == 0) + return TRUE; + } + + g_ptr_array_add (warnings, g_strdup (name)); + + return FALSE; +} + +static const char *const debug_file_directory = DEBUGDIR; + +static ElfParser * +get_build_id_file (ElfParser *elf) +{ + const char *build_id; + GList *tries = NULL, *list; + char *init, *rest; + ElfParser *result = NULL; + char *tmp; + + build_id = elf_parser_get_build_id (elf); + + if (!build_id) + return NULL; + + if (strlen (build_id) < 4) + return NULL; + + init = g_strndup (build_id, 2); + rest = g_strdup_printf ("%s%s", build_id + 2, ".debug"); + + tmp = g_build_filename ( + "/usr", "lib", "debug", ".build-id", init, rest, NULL); + tries = g_list_append (tries, tmp); + + tmp = g_build_filename ( + debug_file_directory, ".build-id", init, rest, NULL); + tries = g_list_append (tries, tmp); + + for (list = tries; list != NULL; list = list->next) + { + char *name = list->data; + ElfParser *parser = elf_parser_new (name, NULL); + + if (parser) + { + const char *file_id = elf_parser_get_build_id (parser); + + if (file_id && strcmp (build_id, file_id) == 0) + { + result = parser; + break; + } + + elf_parser_free (parser); + } + } + + g_list_foreach (tries, (GFunc)g_free, NULL); + g_list_free (tries); + + g_free (init); + g_free (rest); + + return result; +} + +static ElfParser * +get_debuglink_file (ElfParser *elf, + const char *filename, + char **new_name) +{ +#define N_TRIES 4 + const char *basename; + char *dir; + guint32 crc32; + GList *tries = NULL, *list; + ElfParser *result = NULL; + const char *build_id; + + if (!elf) + return NULL; + + basename = elf_parser_get_debug_link (elf, &crc32); + + build_id = elf_parser_get_build_id (elf); + +#if 0 + g_print (" debug link for %s is %s\n", filename, basename); +#endif + + if (!basename) + return NULL; + + dir = g_path_get_dirname (filename); + + tries = g_list_append (tries, g_build_filename (dir, basename, NULL)); + tries = g_list_append (tries, g_build_filename (dir, ".debug", basename, NULL)); + tries = g_list_append (tries, g_build_filename ("/usr", "lib", "debug", dir, basename, NULL)); + tries = g_list_append (tries, g_build_filename (debug_file_directory, dir, basename, NULL)); + + for (list = tries; list != NULL; list = list->next) + { + const char *name = list->data; + ElfParser *parser = elf_parser_new (name, NULL); + guint32 file_crc; + const char *file_build_id; + + if (parser) + { + /* If both files have build ids, and they don't match, + * there is no point computing a CRC32 that we know + * will fail + */ + file_build_id = elf_parser_get_build_id (parser); + if (build_id && file_build_id && strcmp (build_id, file_build_id) != 0) + goto skip; + + file_crc = elf_parser_get_crc32 (parser); + + if (file_crc == crc32) + { + result = parser; + *new_name = g_strdup (name); + break; + } + else + { + if (!already_warned (name)) + { + g_print ("warning: %s has wrong crc %x, %s has crc %x)\n", + name, file_crc, filename, crc32); + } + } + + skip: + elf_parser_free (parser); + } + } + + g_free (dir); + + g_list_foreach (tries, (GFunc)g_free, NULL); + g_list_free (tries); + + return result; +} + +static GList * +get_debug_binaries (GList *files, + ElfParser *elf, + const char *filename) +{ + ElfParser *build_id_file; + GHashTable *seen_names; + GList *free_us = NULL; + + build_id_file = get_build_id_file (elf); + + if (build_id_file) + return g_list_prepend (files, build_id_file); + + /* .gnu_debuglink is actually a chain of debuglinks, and + * there have been real-world cases where following it was + * necessary to get useful debug information. + */ + seen_names = g_hash_table_new (g_str_hash, g_str_equal); + + while (elf) + { + char *debug_name; + + if (g_hash_table_lookup (seen_names, filename)) + break; + + g_hash_table_insert (seen_names, (char *)filename, (char *)filename); + + elf = get_debuglink_file (elf, filename, &debug_name); + + if (elf) + { + files = g_list_prepend (files, elf); + free_us = g_list_prepend (free_us, debug_name); + filename = debug_name; + } + } + + g_list_foreach (free_us, (GFunc)g_free, NULL); + g_list_free (free_us); + + g_hash_table_destroy (seen_names); + + return files; +} + +static char ** +get_lines (const char *format, + ...) + G_GNUC_PRINTF (1, 2); + +static char ** +get_lines (const char *format, + ...) +{ + va_list args; + char *filename; + char **result = NULL; + char *contents; + + va_start (args, format); + filename = g_strdup_vprintf (format, args); + va_end (args); + + if (g_file_get_contents (filename, &contents, NULL, NULL)) + { + result = g_strsplit (contents, "\n", -1); + + g_free (contents); + } + + g_free (filename); + + return result; +} + +static const uint8_t * +get_vdso_bytes (size_t *length) +{ + static const uint8_t *bytes = NULL; + static size_t n_bytes = 0; + static gboolean has_data; + + if (!has_data) + { + char **lines = get_lines ("/proc/%d/maps", getpid()); + int i; + + for (i = 0; lines[i] != NULL; ++i) + { + char file[256]; + gulong start; + gulong end; + int count = sscanf ( + lines[i], "%lx-%lx %*15s %*x %*x:%*x %*u %255s", + &start, &end, file); + + if (count == 3 && strcmp (file, "[vdso]") == 0) + { + n_bytes = end - start; + + /* Dup the memory here so that valgrind will only + * report one 1 byte invalid read instead of + * a ton when the elf parser scans the vdso + * + * The reason we get a spurious invalid read from + * valgrind is that we are getting the address directly + * from /proc/maps, and valgrind knows that its mmap() + * wrapper never returned that address. But since it + * is a legal mapping, it is legal to read it. + */ + bytes = g_memdup ((uint8_t *)start, n_bytes); + + has_data = TRUE; + } + } + } + + if (length) + *length = n_bytes; + + return bytes; +} + +bin_file_t * +bin_file_new (const char *filename) +{ + ElfParser *elf = NULL; + bin_file_t *bf; + + bf = g_new0 (bin_file_t, 1); + + bf->inode_check = FALSE; + bf->filename = g_strdup (filename); + bf->undefined_name = g_strdup_printf ("In file %s", filename); + bf->ref_count = 1; + bf->elf_files = NULL; + + if (strcmp (filename, "[vdso]") == 0) + { + const guint8 *vdso_bytes; + gsize length; + + vdso_bytes = get_vdso_bytes (&length); + + if (vdso_bytes) + elf = elf_parser_new_from_data (vdso_bytes, length); + } + else + { + elf = elf_parser_new (filename, NULL); + } + + if (elf) + { + /* We need the text offset of the actual binary, not the + * (potential) debug binaries + */ + bf->text_offset = elf_parser_get_text_offset (elf); + + bf->elf_files = get_debug_binaries (bf->elf_files, elf, filename); + bf->elf_files = g_list_append (bf->elf_files, elf); + + bf->inode = read_inode (filename); + } + + return bf; +} + +void +bin_file_free (bin_file_t *bin_file) +{ + if (--bin_file->ref_count == 0) + { + g_list_foreach (bin_file->elf_files, (GFunc)elf_parser_free, NULL); + g_list_free (bin_file->elf_files); + + g_free (bin_file->filename); + g_free (bin_file->undefined_name); + g_free (bin_file); + } +} + +const bin_symbol_t * +bin_file_lookup_symbol (bin_file_t *bin_file, + gulong address) +{ + GList *list; + +#if 0 + g_print ("-=-=-=- \n"); + + g_print ("bin file lookup lookup %d\n", address); +#endif + + address -= bin_file->text_offset; + +#if 0 + g_print ("lookup %d in %s\n", address, bin_file->filename); +#endif + + for (list = bin_file->elf_files; list != NULL; list = list->next) + { + ElfParser *elf = list->data; + const ElfSym *sym = elf_parser_lookup_symbol (elf, address); + + if (sym) + { +#if 0 + g_print ("found %lx => %s\n", address, + bin_symbol_get_name (bin_file, sym)); +#endif + return (const bin_symbol_t *)sym; + } + } + +#if 0 + g_print ("%lx undefined in %s (textoffset %x)\n", + address + bin_file->text_offset, + bin_file->filename, + bin_file->text_offset); +#endif + + return (const bin_symbol_t *)bin_file->undefined_name; +} + +gboolean +bin_file_check_inode (bin_file_t *bin_file, + ino_t inode) +{ + if (bin_file->inode == inode) + return TRUE; + + if (!bin_file->elf_files) + return FALSE; + + if (!bin_file->inode_check) + { + g_print ("warning: Inode mismatch for %s (disk: %"G_GUINT64_FORMAT", memory: %"G_GUINT64_FORMAT")\n", + bin_file->filename, (guint64)bin_file->inode, (guint64)inode); + + bin_file->inode_check = TRUE; + } + + return FALSE; +} + +static const ElfSym * +get_elf_sym (bin_file_t *file, + const bin_symbol_t *symbol, + ElfParser **elf_ret) +{ + GList *list; + + for (list = file->elf_files; list != NULL; list = list->next) + { + const ElfSym *sym = (const ElfSym *)symbol; + ElfParser *elf = list->data; + + if (elf_parser_owns_symbol (elf, sym)) + { + *elf_ret = elf; + return sym; + } + } + + g_critical ("Internal error: unrecognized symbol pointer"); + + *elf_ret = NULL; + return NULL; +} + +const char * +bin_symbol_get_name (bin_file_t *file, + const bin_symbol_t *symbol) +{ + if (file->undefined_name == (char *)symbol) + { + return file->undefined_name; + } + else + { + ElfParser *elf; + const ElfSym *sym; + + sym = get_elf_sym (file, symbol, &elf); + + return elf_parser_get_sym_name (elf, sym); + } +} + +gulong +bin_symbol_get_address (bin_file_t *file, + const bin_symbol_t *symbol) +{ + if (file->undefined_name == (char *)symbol) + { + return 0x0; + } + else + { + ElfParser *elf; + const ElfSym *sym; + + sym = get_elf_sym (file, symbol, &elf); + + return elf_parser_get_sym_address (elf, sym); + } +} diff --git a/lib/binfile.h b/lib/util/binfile.h similarity index 65% rename from lib/binfile.h rename to lib/util/binfile.h index 03c3a41e..42a78476 100644 --- a/lib/binfile.h +++ b/lib/util/binfile.h @@ -32,15 +32,15 @@ typedef struct bin_symbol_t bin_symbol_t; /* Binary File */ -bin_file_t * bin_file_new (const char *filename); -void bin_file_free (bin_file_t *bin_file); +bin_file_t * bin_file_new (const char *filename); +void bin_file_free (bin_file_t *bin_file); const bin_symbol_t *bin_file_lookup_symbol (bin_file_t *bin_file, - gulong address); -gboolean bin_file_check_inode (bin_file_t *bin_file, - ino_t inode); -const char * bin_symbol_get_name (bin_file_t *bin_file, - const bin_symbol_t *symbol); -gulong bin_symbol_get_address (bin_file_t *bin_file, - const bin_symbol_t *symbol); + gulong address); +gboolean bin_file_check_inode (bin_file_t *bin_file, + ino_t inode); +const char * bin_symbol_get_name (bin_file_t *bin_file, + const bin_symbol_t *symbol); +gulong bin_symbol_get_address (bin_file_t *bin_file, + const bin_symbol_t *symbol); #endif diff --git a/lib/util/demangle.cpp b/lib/util/demangle.cpp new file mode 100644 index 00000000..1f941cd2 --- /dev/null +++ b/lib/util/demangle.cpp @@ -0,0 +1,40 @@ +/* demangle.cpp + * + * Copyright (C) 2016 Christian Hergert + * + * 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 + +#include "demangle.h" + +gchar * +sysprof_cplus_demangle (const gchar *name) +{ + char *real_name; + gchar *ret; + int status; + + real_name = abi::__cxa_demangle (name, 0, 0, &status); + + if (real_name == NULL) + return NULL; + + ret = g_strdup (real_name); + free (real_name); + + return ret; +} diff --git a/lib/util/demangle.h b/lib/util/demangle.h new file mode 100644 index 00000000..0783ad56 --- /dev/null +++ b/lib/util/demangle.h @@ -0,0 +1,30 @@ +/* demangle.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 DEMANGLE_H +#define DEMANGLE_H + +#include + +G_BEGIN_DECLS + +gchar *sysprof_cplus_demangle (const gchar *name); + +G_END_DECLS + +#endif /* DEMANGLE_H */ diff --git a/lib/util/elfparser.c b/lib/util/elfparser.c new file mode 100644 index 00000000..2d4cb526 --- /dev/null +++ b/lib/util/elfparser.c @@ -0,0 +1,811 @@ +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2006, 2007, Soeren Sandmann + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include + +#include "demangle.h" +#include "elfparser.h" + +typedef struct Section Section; + +struct ElfSym +{ + gulong table; + gulong offset; + gulong address; +}; + +struct Section +{ + const gchar * name; + gsize offset; + gsize size; + gboolean allocated; + gulong load_address; + guint type; +}; + +struct ElfParser +{ + gboolean is_64; + const guchar * data; + gsize length; + + guint n_sections; + Section ** sections; + + guint n_symbols; + ElfSym * symbols; + gsize sym_strings; + + GMappedFile * file; + + char * filename; + + gboolean checked_build_id; + char * build_id; + + const Section * text_section; +}; + +/* FIXME: All of these should in principle do endian swapping, + * but sysprof never has to deal with binaries of a different + * endianness than sysprof itself + */ +#define GET_FIELD(parser, offset, struct_name, idx, field_name) \ + (((parser))->is_64? \ + ((Elf64_ ## struct_name *)(((parser)->data + offset)) + (idx))->field_name : \ + ((Elf32_ ## struct_name *)(((parser)->data + offset)) + (idx))->field_name) + +#define GET_UINT32(parser, offset) \ + *((uint32_t *)(parser->data + offset)) \ + +#define GET_SIZE(parser, struct_name) \ + (((parser)->is_64? \ + sizeof (Elf64_ ## struct_name) : \ + sizeof (Elf32_ ## struct_name))) + +#define MAKE_ELF_UINT_ACCESSOR(field_name) \ + static uint64_t field_name (ElfParser *parser) \ + { \ + return GET_FIELD (parser, 0, Ehdr, 0, field_name); \ + } + +MAKE_ELF_UINT_ACCESSOR (e_shoff) +MAKE_ELF_UINT_ACCESSOR (e_shnum) +MAKE_ELF_UINT_ACCESSOR (e_shstrndx) + +#define MAKE_SECTION_HEADER_ACCESSOR(field_name) \ + static uint64_t field_name (ElfParser *parser, int nth_section) \ + { \ + gsize offset = e_shoff (parser); \ + \ + return GET_FIELD (parser, offset, Shdr, nth_section, field_name); \ + } + +MAKE_SECTION_HEADER_ACCESSOR (sh_name); +MAKE_SECTION_HEADER_ACCESSOR (sh_type); +MAKE_SECTION_HEADER_ACCESSOR (sh_flags); +MAKE_SECTION_HEADER_ACCESSOR (sh_addr); +MAKE_SECTION_HEADER_ACCESSOR (sh_offset); +MAKE_SECTION_HEADER_ACCESSOR (sh_size); + +#define MAKE_SYMBOL_ACCESSOR(field_name) \ + static uint64_t field_name (ElfParser *parser, gulong offset, gulong nth) \ + { \ + return GET_FIELD (parser, offset, Sym, nth, field_name); \ + } + +MAKE_SYMBOL_ACCESSOR(st_name); +MAKE_SYMBOL_ACCESSOR(st_info); +MAKE_SYMBOL_ACCESSOR(st_value); +MAKE_SYMBOL_ACCESSOR(st_size); +MAKE_SYMBOL_ACCESSOR(st_shndx); + +static void +section_free (Section *section) +{ + g_free (section); +} + +static const Section * +find_section (ElfParser *parser, + const char *name, + guint type) +{ + guint i; + + for (i = 0; i < parser->n_sections; ++i) + { + Section *section = parser->sections[i]; + + if (strcmp (section->name, name) == 0 && section->type == type) + return section; + } + + return NULL; +} + +static gboolean +parse_elf_signature (const guchar *data, + gsize length, + gboolean *is_64, + gboolean *is_be) +{ + /* FIXME: this function should be able to return an error */ + if (length < EI_NIDENT) + { + /* FIXME set error */ + return FALSE; + } + + if (data[EI_CLASS] != ELFCLASS32 && + data[EI_CLASS] != ELFCLASS64) + { + /* FIXME set error */ + return FALSE; + } + + if (data[EI_DATA] != ELFDATA2LSB && + data[EI_DATA] != ELFDATA2MSB) + { + /* FIXME set error */ + return FALSE; + } + + if (is_64) + *is_64 = (data[EI_CLASS] == ELFCLASS64); + + if (is_be) + *is_be = (data[EI_DATA] == ELFDATA2MSB); + + return TRUE; +} + +ElfParser * +elf_parser_new_from_data (const guchar *data, + gsize length) +{ + ElfParser *parser; + gboolean is_64, is_big_endian; + int section_names_idx; + const guchar *section_names; + G_GNUC_UNUSED gsize section_headers; + guint i; + + if (!parse_elf_signature (data, length, &is_64, &is_big_endian)) + { + /* FIXME: set error */ + return NULL; + } + + parser = g_new0 (ElfParser, 1); + + parser->is_64 = is_64; + parser->data = data; + parser->length = length; + +#if 0 + g_print (" new parser : %p\n", parser); +#endif + + /* Read ELF header */ + + parser->n_sections = e_shnum (parser); + section_names_idx = e_shstrndx (parser); + section_headers = e_shoff (parser); + + /* Read section headers */ + parser->sections = g_new0 (Section *, parser->n_sections); + + section_names = parser->data + sh_offset (parser, section_names_idx); + + for (i = 0; i < parser->n_sections; ++i) + { + Section *section = g_new (Section, 1); + + section->name = (char *)(section_names + sh_name (parser, i)); + section->size = sh_size (parser, i); + section->offset = sh_offset (parser, i); + section->allocated = !!(sh_flags (parser, i) & SHF_ALLOC); + + if (section->allocated) + section->load_address = sh_addr (parser, i); + else + section->load_address = 0; + + section->type = sh_type (parser, i); + + parser->sections[i] = section; + } + + /* Cache the text section */ + parser->text_section = find_section (parser, ".text", SHT_PROGBITS); + if (!parser->text_section) + parser->text_section = find_section (parser, ".text", SHT_NOBITS); + + parser->filename = NULL; + parser->build_id = NULL; + + return parser; +} + +ElfParser * +elf_parser_new (const char *filename, + GError **err) +{ + const guchar *data; + gsize length; + ElfParser *parser; + + GMappedFile *file = g_mapped_file_new (filename, FALSE, NULL); + + if (!file) + return NULL; + +#if 0 + g_print ("elf parser new : %s\n", filename); +#endif + + data = (guchar *)g_mapped_file_get_contents (file); + length = g_mapped_file_get_length (file); + +#if 0 + g_print ("data %p: for %s\n", data, filename); +#endif + + parser = elf_parser_new_from_data (data, length); + +#if 0 + g_print ("Parser for %s: %p\n", filename, parser); +#endif + + if (!parser) + { + g_mapped_file_unref (file); + return NULL; + } + + parser->filename = g_strdup (filename); + + parser->file = file; + +#if 0 + g_print ("Elf file: %s (debug: %s)\n", + filename, elf_parser_get_debug_link (parser, NULL)); + + if (!parser->symbols) + g_print ("at this point %s has no symbols\n", filename); +#endif + + return parser; +} + +guint32 +elf_parser_get_crc32 (ElfParser *parser) +{ + static const unsigned long crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + const guchar *data; + gsize length; + gulong crc; + gsize i; + + data = parser->data; + length = parser->length; + + crc = 0xffffffff; + + madvise ((char *)data, length, MADV_SEQUENTIAL); + + for (i = 0; i < length; ++i) + crc = crc32_table[(crc ^ data[i]) & 0xff] ^ (crc >> 8); + + /* We just read the entire file into memory, but we only really + * need the symbol table, so swap the whole thing out. + * + * We could be more exact here, but it's only a few minor + * pagefaults. + */ + if (parser->file) + madvise ((char *)data, length, MADV_DONTNEED); + + return ~crc & 0xffffffff; +} + +void +elf_parser_free (ElfParser *parser) +{ + guint i; + + for (i = 0; i < parser->n_sections; ++i) + section_free (parser->sections[i]); + g_free (parser->sections); + + if (parser->file) + g_mapped_file_unref (parser->file); + + g_free (parser->symbols); + + if (parser->filename) + g_free (parser->filename); + + if (parser->build_id) + g_free (parser->build_id); + + g_free (parser); +} + +gchar * +elf_demangle (const char *name) +{ + gchar *demangled = sysprof_cplus_demangle (name); + + if (demangled) + return demangled; + else + return g_strdup (name); +} + +/* + * Looking up symbols + */ +static int +compare_sym (const void *a, const void *b) +{ + const ElfSym *sym_a = a; + const ElfSym *sym_b = b; + + if (sym_a->address < sym_b->address) + return -1; + else if (sym_a->address == sym_b->address) + return 0; + else + return 1; +} + +#if 0 +static void +dump_symbols (ElfParser *parser, ElfSym *syms, guint n_syms) +{ + int i; + + for (i = 0; i < n_syms; ++i) + { + ElfSym *s = &(syms[i]); + + g_print (" %s: %lx\n", elf_parser_get_sym_name (parser, s), s->address); + } +} +#endif + +static void +read_table (ElfParser *parser, + const Section *sym_table, + const Section *str_table) +{ + int sym_size = GET_SIZE (parser, Sym); + guint i, n_symbols; + +#if 0 + g_print ("elf: Reading table for %s\n", parser->filename? parser->filename : ""); +#endif + + parser->n_symbols = sym_table->size / sym_size; + parser->symbols = g_new (ElfSym, parser->n_symbols); + +#if 0 + g_print ("sym table offset: %d\n", sym_table->offset); +#endif + + n_symbols = 0; +#if 0 + g_print ("n syms: %d\n", parser->n_symbols); +#endif + for (i = 0; i < parser->n_symbols; ++i) + { + guint info; + gulong addr; + gulong shndx; + + info = st_info (parser, sym_table->offset, i); + addr = st_value (parser, sym_table->offset, i); + shndx = st_shndx (parser, sym_table->offset, i); + +#if 0 + g_print ("read symbol: %s (section: %d)\n", get_string_indirct (parser->parser, + parser->sym_format, "st_name", + str_table->offset), + shndx); +#endif + + if (addr != 0 && + shndx < parser->n_sections && + parser->sections[shndx] == parser->text_section && + (info & 0xf) == STT_FUNC && + ((info >> 4) == STB_GLOBAL || + (info >> 4) == STB_LOCAL || + (info >> 4) == STB_WEAK)) + { + parser->symbols[n_symbols].address = addr; + parser->symbols[n_symbols].table = sym_table->offset; + parser->symbols[n_symbols].offset = i; + + n_symbols++; + +#if 0 + g_print (" symbol: %s: %lx\n", + get_string_indirect (parser->parser, + parser->sym_format, "st_name", + str_table->offset), + addr - parser->text_section->load_address); + g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n", + addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); +#endif + } + else if (addr != 0) + { +#if 0 + g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n", + addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); +#endif + } + } + + parser->sym_strings = str_table->offset; + parser->n_symbols = n_symbols; + + /* Allocate space for at least one symbol, so that parser->symbols will be + * non-NULL. If it ends up being NULL, we will be parsing the file over and + * over. + */ + parser->symbols = g_renew (ElfSym, parser->symbols, parser->n_symbols + 1); + + qsort (parser->symbols, parser->n_symbols, sizeof (ElfSym), compare_sym); +} + +static void +read_symbols (ElfParser *parser) +{ + const Section *symtab = find_section (parser, ".symtab", SHT_SYMTAB); + const Section *strtab = find_section (parser, ".strtab", SHT_STRTAB); + const Section *dynsym = find_section (parser, ".dynsym", SHT_DYNSYM); + const Section *dynstr = find_section (parser, ".dynstr", SHT_STRTAB); + + if (symtab && strtab) + { +#if 0 + g_print ("reading symbol table of %s\n", parser->filename); +#endif + read_table (parser, symtab, strtab); + } + else if (dynsym && dynstr) + { +#if 0 + g_print ("reading dynamic symbol table of %s\n", parser->filename); +#endif + read_table (parser, dynsym, dynstr); + } + else + { + /* To make sure parser->symbols is non-NULL */ + parser->n_symbols = 0; + parser->symbols = g_new (ElfSym, 1); + } +} + +static ElfSym * +do_lookup (ElfSym *symbols, + gulong address, + int first, + int last) +{ + if (address >= symbols[last].address) + { + return &(symbols[last]); + } + else if (last - first < 3) + { + while (last >= first) + { + if (address >= symbols[last].address) + return &(symbols[last]); + + last--; + } + + return NULL; + } + else + { + int mid = (first + last) / 2; + + if (symbols[mid].address > address) + return do_lookup (symbols, address, first, mid); + else + return do_lookup (symbols, address, mid, last); + } +} + +/* Address should be given in 'offset into text segment' */ +const ElfSym * +elf_parser_lookup_symbol (ElfParser *parser, + gulong address) +{ + const ElfSym *result; + + if (!parser->symbols) + { +#if 0 + g_print ("reading symbols at %p\n", parser); +#endif + read_symbols (parser); + } + + if (parser->n_symbols == 0) + return NULL; + + if (!parser->text_section) + return NULL; + + address += parser->text_section->load_address; + +#if 0 + g_print ("elf: the address we are looking up is %p\n", address); +#endif + + result = do_lookup (parser->symbols, address, 0, parser->n_symbols - 1); + +#if 0 + if (result) + { + g_print (" elf: found %s at %lx\n", elf_parser_get_sym_name (parser, result), result->address); + } + else + { + g_print ("elf: not found\n"); + } +#endif + + if (result) + { + gulong size = st_size (parser, result->table, result->offset); + + if (size > 0 && result->address + size <= address) + { +#if 0 + g_print (" elf: ends at %lx, so rejecting\n", + result->address + size); +#endif + result = NULL; + } + } + + if (result) + { + /* Reject the symbols if the address is outside the text section */ + if (address > parser->text_section->load_address + parser->text_section->size) + result = NULL; + } + + return result; +} + +gulong +elf_parser_get_text_offset (ElfParser *parser) +{ + g_return_val_if_fail (parser != NULL, (gulong)-1); + + if (!parser->text_section) + return (gulong)-1; + + return parser->text_section->offset; +} + +static gchar * +make_hex_string (const guchar *data, int n_bytes) +{ + static const char hex_digits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + GString *string = g_string_new (NULL); + int i; + + for (i = 0; i < n_bytes; ++i) + { + char c = data[i]; + + g_string_append_c (string, hex_digits[(c & 0xf0) >> 4]); + g_string_append_c (string, hex_digits[(c & 0x0f)]); + } + + return g_string_free (string, FALSE); +} + +const gchar * +elf_parser_get_build_id (ElfParser *parser) +{ + if (!parser->checked_build_id) + { + const Section *build_id = + find_section (parser, ".note.gnu.build-id", SHT_NOTE); + guint64 name_size; + guint64 desc_size; + guint64 type; + const char *name; + guint64 offset; + + parser->checked_build_id = TRUE; + + if (!build_id) + return NULL; + + offset = build_id->offset; + + name_size = GET_FIELD (parser, offset, Nhdr, 0, n_namesz); + desc_size = GET_FIELD (parser, offset, Nhdr, 0, n_descsz); + type = GET_FIELD (parser, offset, Nhdr, 0, n_type); + + offset += GET_SIZE (parser, Nhdr); + + name = (char *)(parser->data + offset); + + if (strncmp (name, ELF_NOTE_GNU, name_size) != 0 || type != NT_GNU_BUILD_ID) + return NULL; + + offset += strlen (name); + + offset = (offset + 3) & (~0x3); + + parser->build_id = make_hex_string (parser->data + offset, desc_size); + } + + return parser->build_id; +} + +const char * +elf_parser_get_debug_link (ElfParser *parser, guint32 *crc32) +{ + guint64 offset; + const Section *debug_link = find_section (parser, ".gnu_debuglink", + SHT_PROGBITS); + const gchar *result; + + if (!debug_link) + return NULL; + + offset = debug_link->offset; + + result = (char *)(parser->data + offset); + + if (crc32) + { + int len = strlen (result) + 1; + offset = (offset + len + 3) & ~0x3; + + *crc32 = GET_UINT32 (parser, offset); + } + + return result; +} + +static const guchar * +get_section (ElfParser *parser, + const char *name) +{ + const Section *section = find_section (parser, name, SHT_PROGBITS); + + if (section) + return parser->data + section->offset; + else + return NULL; +} + +const guchar * +elf_parser_get_eh_frame (ElfParser *parser) +{ + return get_section (parser, ".eh_frame"); +} + +const guchar * +elf_parser_get_debug_frame (ElfParser *parser) +{ + return get_section (parser, ".debug_frame"); +} + +const char * +elf_parser_get_sym_name (ElfParser *parser, + const ElfSym *sym) +{ + g_return_val_if_fail (parser != NULL, NULL); + + return (char *)(parser->data + parser->sym_strings + + st_name (parser, sym->table, sym->offset)); +} + +gboolean +elf_parser_owns_symbol (ElfParser *parser, + const ElfSym *sym) +{ + ElfSym *first, *last; + + if (!parser->n_symbols) + return FALSE; + + first = parser->symbols; + last = parser->symbols + parser->n_symbols - 1; + + return first <= sym && sym <= last; +} + +gulong +elf_parser_get_sym_address (ElfParser *parser, + const ElfSym *sym) +{ + return sym->address - parser->text_section->load_address; +} + +/* + * Utility functions + */ diff --git a/lib/elfparser.h b/lib/util/elfparser.h similarity index 100% rename from lib/elfparser.h rename to lib/util/elfparser.h diff --git a/lib/stackstash.c b/lib/util/stackstash.c similarity index 61% rename from lib/stackstash.c rename to lib/util/stackstash.c index 44912e4b..9b333598 100644 --- a/lib/stackstash.c +++ b/lib/util/stackstash.c @@ -21,41 +21,41 @@ struct StackStash { - int ref_count; - StackNode * root; - GHashTable * nodes_by_data; - GDestroyNotify destroy; + int ref_count; + StackNode * root; + GHashTable * nodes_by_data; + GDestroyNotify destroy; - StackNode * cached_nodes; - GPtrArray * blocks; + StackNode * cached_nodes; + GPtrArray * blocks; }; static void decorate_node (StackNode *node, - StackStash *stash) + StackStash *stash) { StackNode *n; if (!node) - return; + return; decorate_node (node->siblings, stash); decorate_node (node->children, stash); node->next = g_hash_table_lookup (stash->nodes_by_data, &node->data); g_hash_table_insert (stash->nodes_by_data, &node->data, node); - + /* FIXME: This could be done more efficiently * by keeping track of the ancestors we have seen. */ node->toplevel = TRUE; for (n = node->parent; n != NULL; n = n->parent) { - if (n->data == node->data) - { - node->toplevel = FALSE; - break; - } + if (n->data == node->data) + { + node->toplevel = FALSE; + break; + } } } @@ -80,7 +80,7 @@ static void stack_stash_decorate (StackStash *stash) { if (stash->nodes_by_data) - return; + return; stash->nodes_by_data = g_hash_table_new (address_hash, address_equal); @@ -89,8 +89,8 @@ stack_stash_decorate (StackStash *stash) static void free_key (gpointer key, - gpointer value, - gpointer data) + gpointer value, + gpointer data) { GDestroyNotify destroy = data; uint64_t u64 = *(uint64_t *)key; @@ -103,14 +103,14 @@ stack_stash_undecorate (StackStash *stash) { if (stash->nodes_by_data) { - if (stash->destroy) - { - g_hash_table_foreach ( - stash->nodes_by_data, free_key, stash->destroy); - } - - g_hash_table_destroy (stash->nodes_by_data); - stash->nodes_by_data = NULL; + if (stash->destroy) + { + g_hash_table_foreach ( + stash->nodes_by_data, free_key, stash->destroy); + } + + g_hash_table_destroy (stash->nodes_by_data); + stash->nodes_by_data = NULL; } } @@ -118,7 +118,7 @@ static GHashTable * get_nodes_by_data (StackStash *stash) { if (!stash->nodes_by_data) - stack_stash_decorate (stash); + stack_stash_decorate (stash); return stash->nodes_by_data; } @@ -133,16 +133,16 @@ stack_node_new (StackStash *stash) #define BLOCK_SIZE 32768 #define N_NODES (BLOCK_SIZE / sizeof (StackNode)) - StackNode *block = g_malloc (BLOCK_SIZE); - int i; + StackNode *block = g_malloc (BLOCK_SIZE); + guint i; - for (i = 0; i < N_NODES; ++i) - { - block[i].next = stash->cached_nodes; - stash->cached_nodes = &(block[i]); - } + for (i = 0; i < N_NODES; ++i) + { + block[i].next = stash->cached_nodes; + stash->cached_nodes = &(block[i]); + } - g_ptr_array_add (stash->blocks, block); + g_ptr_array_add (stash->blocks, block); } node = stash->cached_nodes; @@ -155,7 +155,7 @@ stack_node_new (StackStash *stash) node->size = 0; node->next = NULL; node->total = 0; - + return node; } @@ -169,7 +169,7 @@ create_stack_stash (GDestroyNotify destroy) stash->nodes_by_data = NULL; stash->ref_count = 1; stash->destroy = destroy; - + stash->cached_nodes = NULL; stash->blocks = g_ptr_array_new (); @@ -187,76 +187,76 @@ stack_stash_new (GDestroyNotify destroy) static void stack_stash_free (StackStash *stash) { - int i; - + guint i; + stack_stash_undecorate (stash); for (i = 0; i < stash->blocks->len; ++i) - g_free (stash->blocks->pdata[i]); - + g_free (stash->blocks->pdata[i]); + g_ptr_array_free (stash->blocks, TRUE); - + g_free (stash); } StackNode * -stack_stash_add_trace (StackStash *stash, - uint64_t *addrs, - int n_addrs, - int size) +stack_stash_add_trace (StackStash *stash, + const uint64_t *addrs, + int n_addrs, + int size) { StackNode **location = &(stash->root); StackNode *parent = NULL; int i; if (!n_addrs) - return NULL; + return NULL; if (stash->nodes_by_data) - stack_stash_undecorate (stash); - + stack_stash_undecorate (stash); + for (i = n_addrs - 1; i >= 0; --i) { - StackNode *match = NULL; - StackNode *prev; + StackNode *match = NULL; + StackNode *prev; - /* FIXME: On x86-64 we don't get proper stacktraces which means - * each node can have tons of children. That makes this loop - * here show up on profiles. - * - * Not sure what can be done about it aside from actually fixing - * x86-64 to get stacktraces. - */ - prev = NULL; - for (match = *location; match; prev = match, match = match->siblings) - { - if (match->data == addrs[i]) - { - if (prev) - { - /* move to front */ - prev->siblings = match->siblings; - match->siblings = *location; - *location = match; - } - - break; - } - } + /* FIXME: On x86-64 we don't get proper stacktraces which means + * each node can have tons of children. That makes this loop + * here show up on profiles. + * + * Not sure what can be done about it aside from actually fixing + * x86-64 to get stacktraces. + */ + prev = NULL; + for (match = *location; match; prev = match, match = match->siblings) + { + if (match->data == addrs[i]) + { + if (prev) + { + /* move to front */ + prev->siblings = match->siblings; + match->siblings = *location; + *location = match; + } - if (!match) - { - match = stack_node_new (stash); - match->data = addrs[i]; - match->siblings = *location; - match->parent = parent; - *location = match; - } + break; + } + } - match->total += size; + if (!match) + { + match = stack_node_new (stash); + match->data = addrs[i]; + match->siblings = *location; + match->parent = parent; + *location = match; + } - location = &(match->children); - parent = match; + match->total += size; + + location = &(match->children); + parent = match; } parent->size += size; @@ -266,46 +266,46 @@ stack_stash_add_trace (StackStash *stash, static void do_callback (StackNode *node, - StackLink *trace, - StackFunction func, - gpointer data) + StackLink *trace, + StackFunction func, + gpointer data) { StackLink link; if (trace) - trace->prev = &link; - + trace->prev = &link; + link.next = trace; link.prev = NULL; - + while (node) { - link.data = node->data; - - if (node->size) - func (&link, node->size, data); - - do_callback (node->children, &link, func, data); - - node = node->siblings; + link.data = node->data; + + if (node->size) + func (&link, node->size, data); + + do_callback (node->children, &link, func, data); + + node = node->siblings; } if (trace) - trace->prev = NULL; + trace->prev = NULL; } void stack_stash_foreach (StackStash *stash, - StackFunction stack_func, - gpointer data) + StackFunction stack_func, + gpointer data) { do_callback (stash->root, NULL, stack_func, data); } void stack_node_foreach_trace (StackNode *node, - StackFunction func, - gpointer data) + StackFunction func, + gpointer data) { StackLink link; @@ -314,8 +314,8 @@ stack_node_foreach_trace (StackNode *node, link.prev = NULL; if (node->size) - func (&link, node->size, data); - + func (&link, node->size, data); + do_callback (node->children, &link, func, data); } @@ -324,7 +324,7 @@ stack_stash_unref (StackStash *stash) { stash->ref_count--; if (stash->ref_count == 0) - stack_stash_free (stash); + stack_stash_free (stash); } StackStash * @@ -336,19 +336,19 @@ stack_stash_ref (StackStash *stash) StackNode * stack_stash_find_node (StackStash *stash, - gpointer data) + gpointer data) { uint64_t u64 = POINTER_TO_U64 (data); - + g_return_val_if_fail (stash != NULL, NULL); - + return g_hash_table_lookup (get_nodes_by_data (stash), &u64); } typedef struct { StackNodeFunc func; - gpointer data; + gpointer data; } Info; static void @@ -361,13 +361,13 @@ do_foreach (gpointer key, gpointer value, gpointer data) void stack_stash_foreach_by_address (StackStash *stash, - StackNodeFunc func, - gpointer data) + StackNodeFunc func, + gpointer data) { Info info; info.func = func; info.data = data; - + g_hash_table_foreach (get_nodes_by_data (stash), do_foreach, &info); } @@ -379,9 +379,9 @@ stack_stash_get_root (StackStash *stash) void stack_stash_set_root (StackStash *stash, - StackNode *root) + StackNode *root) { g_return_if_fail (stash->root == NULL); - + stash->root = root; } diff --git a/lib/stackstash.h b/lib/util/stackstash.h similarity index 56% rename from lib/stackstash.h rename to lib/util/stackstash.h index 4fad001e..9d2909bf 100644 --- a/lib/stackstash.h +++ b/lib/util/stackstash.h @@ -27,60 +27,59 @@ typedef struct StackStash StackStash; typedef struct StackNode StackNode; typedef struct StackLink StackLink; -#define U64_TO_POINTER(u) ((void *)(intptr_t)u) -#define POINTER_TO_U64(p) ((uint64_t)(intptr_t)p) +#define U64_TO_POINTER(u) ((void *)(intptr_t)u) +#define POINTER_TO_U64(p) ((uint64_t)(intptr_t)p) struct StackNode { - uint64_t data; + uint64_t data; - guint total : 32; - guint size : 31; - guint toplevel : 1; - - StackNode * parent; - StackNode * siblings; - StackNode * children; + guint total : 32; + guint size : 31; + guint toplevel : 1; + + StackNode * parent; + StackNode * siblings; + StackNode * children; StackNode * next; }; struct StackLink { - uint64_t data; + uint64_t data; StackLink *next; StackLink *prev; }; typedef void (* StackFunction) (StackLink *trace, - gint size, - gpointer data); + gint size, + gpointer data); typedef void (* StackNodeFunc) (StackNode *node, - gpointer data); + gpointer data); -/* Stach */ StackStash *stack_stash_new (GDestroyNotify destroy); -StackNode * stack_node_new (StackStash *stash); -StackNode * stack_stash_add_trace (StackStash *stash, - uint64_t *addrs, - gint n_addrs, - int size); +StackNode *stack_node_new (StackStash *stash); +StackNode *stack_stash_add_trace (StackStash *stash, + const uint64_t *addrs, + gint n_addrs, + int size); void stack_stash_foreach (StackStash *stash, - StackFunction stack_func, - gpointer data); + StackFunction stack_func, + gpointer data); void stack_node_foreach_trace (StackNode *node, - StackFunction stack_func, - gpointer data); + StackFunction stack_func, + gpointer data); StackNode *stack_stash_find_node (StackStash *stash, - gpointer address); + gpointer address); void stack_stash_foreach_by_address (StackStash *stash, - StackNodeFunc func, - gpointer data); + StackNodeFunc func, + gpointer data); StackNode *stack_stash_get_root (StackStash *stash); StackStash *stack_stash_ref (StackStash *stash); void stack_stash_unref (StackStash *stash); -void stack_stash_set_root (StackStash *stash, - StackNode *root); +void stack_stash_set_root (StackStash *stash, + StackNode *root); #endif diff --git a/lib/util/util.h b/lib/util/util.h new file mode 100644 index 00000000..591722db --- /dev/null +++ b/lib/util/util.h @@ -0,0 +1,32 @@ +#ifndef SP_UTIL_H +#define SP_UTIL_H + +#if defined(__i386__) +#define read_barrier() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") +#endif + +#if defined(__x86_64__) +#define read_barrier() asm volatile("lfence" ::: "memory") +#endif + +#ifdef __powerpc__ +#define read_barrier() asm volatile ("sync" ::: "memory") +#endif + +#ifdef __s390__ +#define read_barrier() asm volatile("bcr 15,0" ::: "memory") +#endif + +#ifdef __sh__ +#if defined(__SH4A__) || defined(__SH5__) +# define read_barrier() asm volatile("synco" ::: "memory") +#else +# define read_barrier() asm volatile("" ::: "memory") +#endif +#endif + +#ifdef __hppa__ +#define read_barrier() asm volatile("" ::: "memory") +#endif + +#endif /* SP_UTIL_H */ diff --git a/lib/watch.c b/lib/watch.c deleted file mode 100644 index b1e4824f..00000000 --- a/lib/watch.c +++ /dev/null @@ -1,328 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */ - -/* - Library for asynchronous communication - * Copyright (C) 2002 Søren Sandmann (sandmann@daimi.au.dk) - * - * 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 2 of the License, 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; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include "watch.h" - -typedef struct Watch Watch; - -struct Watch { - GSource source; - GPollFD poll_fd; - gboolean removed; - - WatchCallback read_callback; - WatchCallback write_callback; - WatchCallback hangup_callback; - WatchCallback error_callback; - WatchCallback priority_callback; - - gpointer data; -}; - -static GHashTable *watched_fds; - -static void -init (void) -{ - if (!watched_fds) - watched_fds = g_hash_table_new (g_int_hash, g_int_equal); -} - -static Watch * -lookup_watch (gint fd) -{ - init (); - - return g_hash_table_lookup (watched_fds, &fd); -} - -static void -internal_add_watch (Watch *watch) -{ - gpointer fd = &(watch->poll_fd.fd); - - init (); - - g_hash_table_insert (watched_fds, fd, watch); - g_source_add_poll ((GSource *)watch, &(watch->poll_fd)); -} - -static void -internal_remove_watch (Watch *watch) -{ - gpointer fd = &(watch->poll_fd.fd); - - init (); - - watch->removed = TRUE; - g_source_remove_poll ((GSource *)watch, &(watch->poll_fd)); - g_hash_table_remove (watched_fds, fd); -} - -static gboolean -watch_prepare (GSource *source, - gint *timeout) -{ - *timeout = -1; - - return FALSE; -} - -static gboolean -watch_check (GSource *source) -{ - Watch *watch = (Watch *)source; - gint revents = watch->poll_fd.revents; - - if (revents & (G_IO_NVAL)) - { - /* This can happen if the user closes the file descriptor - * without first removing the watch. We silently ignore it - */ - internal_remove_watch (watch); - g_source_unref (source); - return FALSE; - } - - if ((revents & G_IO_HUP) && watch->hangup_callback) - return TRUE; - - if ((revents & G_IO_IN) && watch->read_callback) - return TRUE; - - if ((revents & G_IO_PRI) && watch->priority_callback) - return TRUE; - - if ((revents & G_IO_ERR) && watch->error_callback) - return TRUE; - - if ((revents & G_IO_OUT) && watch->write_callback) - return TRUE; - - return FALSE; -} - -static gboolean -watch_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - Watch *watch = (Watch *)source; - gint revents = watch->poll_fd.revents; - gboolean removed; - - g_source_ref (source); - - if (!watch->removed && (revents & G_IO_IN) && watch->read_callback) - watch->read_callback (watch->data); - - if (!watch->removed && (revents & G_IO_PRI) && watch->priority_callback) - watch->priority_callback (watch->data); - - if (!watch->removed && (revents & G_IO_OUT) && watch->write_callback) - watch->write_callback (watch->data); - - if (!watch->removed && (revents & G_IO_ERR) && watch->error_callback) - watch->error_callback (watch->data); - - if (!watch->removed && (revents & G_IO_HUP) && watch->hangup_callback) - watch->hangup_callback (watch->data); - - removed = watch->removed; - - g_source_unref (source); - - if (removed) - return FALSE; - - return TRUE; -} - -static void -watch_finalize (GSource *source) -{ -} - -static void -update_poll_mask (Watch *watch) -{ - gint events = 0; - - g_source_remove_poll ((GSource *)watch, &(watch->poll_fd)); - - if (watch->read_callback) - events |= G_IO_IN; - - if (watch->write_callback) - events |= G_IO_OUT; - - if (watch->priority_callback) - events |= G_IO_PRI; - - if (watch->hangup_callback) - events |= G_IO_HUP; - - if (watch->error_callback) - events |= G_IO_ERR; - - watch->poll_fd.events = events; - - g_source_add_poll ((GSource *)watch, &(watch->poll_fd)); -} - -void -fd_add_watch (gint fd, - gpointer data) -{ - static GSourceFuncs watch_funcs = { - watch_prepare, - watch_check, - watch_dispatch, - watch_finalize, - }; - Watch *watch = lookup_watch (fd); - - g_return_if_fail (fd > 0); - g_return_if_fail (watch == NULL); - - watch = (Watch *)g_source_new (&watch_funcs, sizeof (Watch)); - g_source_set_can_recurse ((GSource *)watch, TRUE); - g_source_attach ((GSource *)watch, NULL); - - watch->poll_fd.fd = fd; - watch->poll_fd.events = 0; - watch->removed = FALSE; - - watch->read_callback = NULL; - watch->write_callback = NULL; - watch->hangup_callback = NULL; - watch->error_callback = NULL; - watch->priority_callback = NULL; - - watch->data = data; - - internal_add_watch (watch); -} - -void -fd_set_read_callback (gint fd, - WatchCallback read_cb) -{ - Watch *watch = lookup_watch (fd); - - g_return_if_fail (fd > 0); - g_return_if_fail (watch != NULL); - - if (watch->read_callback != read_cb) - { - watch->read_callback = read_cb; - update_poll_mask (watch); - } -} - -void -fd_set_write_callback (gint fd, - WatchCallback write_cb) -{ - Watch *watch = lookup_watch (fd); - - g_return_if_fail (fd > 0); - g_return_if_fail (watch != NULL); - - if (watch->write_callback != write_cb) - { - watch->write_callback = write_cb; - update_poll_mask (watch); - } -} - -void -fd_set_hangup_callback (gint fd, - WatchCallback hangup_cb) -{ - Watch *watch = lookup_watch (fd); - - g_return_if_fail (fd > 0); - g_return_if_fail (watch != NULL); - - if (watch->hangup_callback != hangup_cb) - { - watch->hangup_callback = hangup_cb; - update_poll_mask (watch); - } -} - -void -fd_set_error_callback (gint fd, - WatchCallback error_cb) -{ - Watch *watch = lookup_watch (fd); - - g_return_if_fail (fd > 0); - g_return_if_fail (watch != NULL); - - if (watch->error_callback != error_cb) - { - watch->error_callback = error_cb; - update_poll_mask (watch); - } -} - -void -fd_set_priority_callback (gint fd, - WatchCallback priority_cb) -{ - Watch *watch = lookup_watch (fd); - - g_return_if_fail (fd > 0); - g_return_if_fail (watch != NULL); - - if (watch->priority_callback != priority_cb) - { - watch->priority_callback = priority_cb; - update_poll_mask (watch); - } -} - -void -fd_remove_watch (gint fd) -{ - Watch *watch = lookup_watch (fd); - - g_return_if_fail (fd > 0); - - if (!watch) - return; - - internal_remove_watch (watch); - g_source_unref ((GSource *)watch); -} - -gboolean -fd_is_watched (gint fd) -{ - g_return_val_if_fail (fd > 0, FALSE); - - if (lookup_watch (fd)) - return TRUE; - else - return FALSE; -} diff --git a/lib/watch.h b/lib/watch.h deleted file mode 100644 index 78bac2d6..00000000 --- a/lib/watch.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Library for asynchronous communication - * Copyright (C) 2002 Søren Sandmann (sandmann@daimi.au.dk) - * - * 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 2 of the License, 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; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -/* - * Watching file descriptors - */ -typedef void (* WatchCallback) (gpointer data); - -void fd_add_watch (gint fd, - gpointer data); -void fd_set_read_callback (gint fd, - WatchCallback read_cb); -void fd_set_write_callback (gint fd, - WatchCallback write_cb); -void fd_set_hangup_callback (gint fd, - WatchCallback hangup_cb); -void fd_set_error_callback (gint fd, - WatchCallback error_cb); -void fd_set_priority_callback (gint fd, - WatchCallback priority_cb); -void fd_remove_watch (gint fd); -gboolean fd_is_watched (gint fd); - diff --git a/lib/xmlstore.c b/lib/xmlstore.c deleted file mode 100644 index 71aaa164..00000000 --- a/lib/xmlstore.c +++ /dev/null @@ -1,156 +0,0 @@ -typedef struct ParsedItem ParsedItem; - -struct XmlItem -{ - gboolean is_element; - - union - { - struct - { - const char *name; - guint sibling_index; - } element; - - char text[1]; - } u; -}; - -struct XmlStore -{ - GHashTable *names; - GArray *items; - - GList *stack; - guint last_index; -}; - -static guint -add_item (GArray *array, - const XmlItem *item) -{ - -} - -XmlStore * -xml_store_new (void) -{ - XmlStore *store = g_new (XmlStore, 1); - store->names = g_hash_table_new (g_str_hash, g_str_equal); - store->stack = NULL; - store->items = g_array_new (TRUE, TRUE, sizeof (XmlItem)); -} - -void -xml_store_append_begin (XmlStore *store, - const char *element) -{ - XmlItem item; - - item.is_element = TRUE; - item.element.name = canonical_name (store, element); - item.element.sibling = NULL; - - if (store->last) - store->last->u.element.sibling = &result; - -} - - -typedef enum -{ - BEGIN, - TEXT, - END -} XmlItemType; - -struct XmlItem -{ - XmlItemType type; - char [1] data; -}; - -struct XmlStore -{ - XmlItem * items; - - GHashTable *user_data_map; -}; - -struct ParsedItem -{ - XmlItemType type; - - union - { - struct - { - char * element; - int n_attrs; - char **attr; - } begin_item; - - struct - { - char *text; - } text_item; - - struct - { - char *element; - } end_item; - } u; -}; - -static void -parse_begin_item (XmlItem *item, - ParsedItem *parsed_item) -{ - -} - -static void -parse_end_item (XmlItem *item, - ParsedItem *parsed_item) -{ - -} - -static void -parse_text_item (XmlItem *item, - ParsedItem *parsed_item) -{ - -} - -static ParsedItem * -parsed_item_new (XmlItem *item) -{ - ParsedItem *parsed_item = g_new0 (ParsedItem, 0); - - switch (item->type) - { - case BEGIN: - parsed_item->type = BEGIN; - parse_begin_item (item, parsed_item); - break; - - case END: - parsed_item->type = END; - parse_end_item (item, parsed_item); - break; - - case TEXT: - parsed_item->type = TEXT; - parse_text_item (item, parsed_item); - break; - } - - return parsed_item; -} - -static void -parsed_item_free (ParsedItem *item) -{ - -} diff --git a/lib/xmlstore.h b/lib/xmlstore.h deleted file mode 100644 index dd167d03..00000000 --- a/lib/xmlstore.h +++ /dev/null @@ -1,58 +0,0 @@ -typedef struct XmlIter XmlIter; -typedef struct XmlStore XmlStore; - -XmlStore *xml_store_new (void); -void xml_store_free (XmlStore *store); -void xml_store_append_begin (XmlStore *store, - const char *element); -void xml_store_append_end (XmlStore *store, - const char *element); -void xml_store_append_text (XmlStore *store, - const char *text); -void xml_store_write (XmlStore *store, - const char *file, - GError *file); -void xml_store_set_data (XmlIter *iter, - gpointer data); -gpointer xml_store_get_data (XmlIter *iter, - gpointer data); -void xml_store_has_data (XmlIter *iter); - -/* An iter stays valid as long as the XmlStore is valid */ -XmlIter * xml_store_get_iter (XmlStore *store); -XmlIter * xml_iter_get_sibling (XmlIter *iter); -XmlIter * xml_iter_get_child (XmlIter *iter); -int xml_iter_get_n_children (XmlIter *iter); -gboolean xml_iter_valid (XmlIter *iter); - - -void -process_tree (XmlIter *iter) -{ - XmlIter *i; - - if (!xml_iter_valid (iter)) - return; - - /* siblings */ - i = xml_iter_sibling (iter); - while (xml_iter_valid (i)) - { - process_tree (i); - - i = xml_iter_sibling (i); - } - - /* children */ - process_tree (xml_iter_child (iter)); - - - process_tree (xml_iter_sibling (iter)); - process_tree (xml_iter_child (iter)); -} - -void -process_store (XmlStore *store) -{ - process_tree (xml_store_get_iter (store)); -} diff --git a/m4/Makefile.am b/m4/Makefile.am new file mode 100644 index 00000000..ae44c94b --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1 @@ +-include $(top_srcdir)/git.mk diff --git a/m4/appstream-xml.m4 b/m4/appstream-xml.m4 new file mode 100644 index 00000000..1472802b --- /dev/null +++ b/m4/appstream-xml.m4 @@ -0,0 +1,86 @@ +# appstream-xml.m4 +# +# serial 6 + +dnl APPSTREAM_XML +dnl Installs and validates AppData XML files. +dnl +dnl Call APPSTREAM_XML in configure.ac to check for the appstream-util tool. +dnl Add @APPSTREAM_XML_RULES@ to a Makefile.am to substitute the make rules. Add +dnl .appdata.xml files to appstream_XML in Makefile.am and they will be validated +dnl at make check time, if appstream-util is installed, as well as installed +dnl to the correct location automatically. Add --enable-appstream-util to +dnl DISTCHECK_CONFIGURE_FLAGS in Makefile.am to require valid AppData XML when +dnl doing a distcheck. +dnl +dnl Adding files to appstream_XML does not distribute them automatically. + +AC_DEFUN([APPSTREAM_XML], +[ + m4_pattern_allow([AM_V_GEN]) + AC_ARG_ENABLE([appstream-util], + [AS_HELP_STRING([--disable-appstream-util], + [Disable validating AppData XML files during check phase])]) + + AS_IF([test "x$enable_appstream_validate" != "xno"], + [AC_PATH_PROG([APPSTREAM_UTIL], [appstream-util]) + AS_IF([test "x$APPSTREAM_UTIL" = "x"], + [have_appstream_validate=no], + [have_appstream_validate=yes + AC_SUBST([APPSTREAM_UTIL])])], + [have_appstream_validate=no]) + + AS_IF([test "x$have_appstream_validate" != "xno"], + [appstream_validate=yes], + [appstream_validate=no + AS_IF([test "x$enable_appstream_validate" = "xyes"], + [AC_MSG_ERROR([AppData validation was requested but appstream-util was not found])])]) + + AC_SUBST([appstreamxmldir], [${datadir}/appdata]) + + APPSTREAM_XML_RULES=' +.PHONY : uninstall-appstream-xml install-appstream-xml clean-appstream-xml + +mostlyclean-am: clean-appstream-xml + +%.appdata.valid: %.appdata.xml + $(AM_V_GEN) if test -f "$<"; then d=; else d="$(srcdir)/"; fi; \ + if test -n "$(APPSTREAM_UTIL)"; \ + then $(APPSTREAM_UTIL) --nonet validate $${d}$<; fi \ + && touch [$]@ + +check-am: $(appstream_XML:.appdata.xml=.appdata.valid) +uninstall-am: uninstall-appstream-xml +install-data-am: install-appstream-xml + +.SECONDARY: $(appstream_XML) + +install-appstream-xml: $(appstream_XML) + @$(NORMAL_INSTALL) + if test -n "$^"; then \ + test -z "$(appstreamxmldir)" || $(MKDIR_P) "$(DESTDIR)$(appstreamxmldir)"; \ + $(INSTALL_DATA) $^ "$(DESTDIR)$(appstreamxmldir)"; \ + fi + +uninstall-appstream-xml: + @$(NORMAL_UNINSTALL) + @list='\''$(appstream_XML)'\''; test -n "$(appstreamxmldir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e '\''s|^.*/||'\''`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '\''$(DESTDIR)$(appstreamxmldir)'\'' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(appstreamxmldir)" && rm -f $$files + +clean-appstream-xml: + rm -f $(appstream_XML:.appdata.xml=.appdata.valid) +' + _APPSTREAM_XML_SUBST(APPSTREAM_XML_RULES) +]) + +dnl _APPSTREAM_XML_SUBST(VARIABLE) +dnl Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST +AC_DEFUN([_APPSTREAM_XML_SUBST], +[ +AC_SUBST([$1]) +m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])]) +] +) diff --git a/m4/ax_append_compile_flags.m4 b/m4/ax_append_compile_flags.m4 new file mode 100644 index 00000000..2bb27ef2 --- /dev/null +++ b/m4/ax_append_compile_flags.m4 @@ -0,0 +1,67 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# For every FLAG1, FLAG2 it is checked whether the compiler works with the +# flag. If it does, the flag is added FLAGS-VARIABLE +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. During the check the flag is always added to the +# current language's flags. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: This macro depends on the AX_APPEND_FLAG and +# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with +# AX_APPEND_LINK_FLAGS. +# +# LICENSE +# +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_APPEND_COMPILE_FLAGS], +[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) +AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) +for flag in $1; do + AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4]) +done +])dnl AX_APPEND_COMPILE_FLAGS diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4 new file mode 100644 index 00000000..08f2e07e --- /dev/null +++ b/m4/ax_append_flag.m4 @@ -0,0 +1,71 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_append_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) +# +# DESCRIPTION +# +# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space +# added in between. +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains +# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly +# FLAG. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 6 + +AC_DEFUN([AX_APPEND_FLAG], +[dnl +AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF +AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) +AS_VAR_SET_IF(FLAGS,[ + AS_CASE([" AS_VAR_GET(FLAGS) "], + [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], + [ + AS_VAR_APPEND(FLAGS,[" $1"]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) + ], + [ + AS_VAR_SET(FLAGS,[$1]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) +AS_VAR_POPDEF([FLAGS])dnl +])dnl AX_APPEND_FLAG diff --git a/m4/ax_append_link_flags.m4 b/m4/ax_append_link_flags.m4 new file mode 100644 index 00000000..fd70fc72 --- /dev/null +++ b/m4/ax_append_link_flags.m4 @@ -0,0 +1,65 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# For every FLAG1, FLAG2 it is checked whether the linker works with the +# flag. If it does, the flag is added FLAGS-VARIABLE +# +# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is +# used. During the check the flag is always added to the linker's flags. +# +# If EXTRA-FLAGS is defined, it is added to the linker's default flags +# when the check is done. The check is thus made with the flags: "LDFLAGS +# EXTRA-FLAGS FLAG". This can for example be used to force the linker to +# issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG. +# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS. +# +# LICENSE +# +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_APPEND_LINK_FLAGS], +[AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) +AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) +for flag in $1; do + AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3], [$4]) +done +])dnl AX_APPEND_LINK_FLAGS diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 00000000..ca363971 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 4 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_check_enable_debug.m4 b/m4/ax_check_enable_debug.m4 new file mode 100644 index 00000000..f99d75fe --- /dev/null +++ b/m4/ax_check_enable_debug.m4 @@ -0,0 +1,124 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE]) +# +# DESCRIPTION +# +# Check for the presence of an --enable-debug option to configure, with +# the specified default value used when the option is not present. Return +# the value in the variable $ax_enable_debug. +# +# Specifying 'yes' adds '-g -O0' to the compilation flags for all +# languages. Specifying 'info' adds '-g' to the compilation flags. +# Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to +# the linking flags. Otherwise, nothing is added. +# +# Define the variables listed in the second argument if debug is enabled, +# defaulting to no variables. Defines the variables listed in the third +# argument if debug is disabled, defaulting to NDEBUG. All lists of +# variables should be space-separated. +# +# If debug is not enabled, ensure AC_PROG_* will not add debugging flags. +# Should be invoked prior to any AC_PROG_* compiler checks. +# +# IS-RELEASE can be used to change the default to 'no' when making a +# release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it +# uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE +# macro, there is no need to pass this parameter. +# +# AX_IS_RELEASE([git-directory]) +# AX_CHECK_ENABLE_DEBUG() +# +# LICENSE +# +# Copyright (c) 2011 Rhys Ulerich +# Copyright (c) 2014, 2015 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. + +#serial 5 + +AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[ + AC_BEFORE([$0],[AC_PROG_CC])dnl + AC_BEFORE([$0],[AC_PROG_CXX])dnl + AC_BEFORE([$0],[AC_PROG_F77])dnl + AC_BEFORE([$0],[AC_PROG_FC])dnl + + AC_MSG_CHECKING(whether to enable debugging) + + ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1]))) + ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],, + [$ax_is_release], + [$4]))) + + # If this is a release, override the default. + AS_IF([test "$ax_enable_debug_is_release" = "yes"], + [ax_enable_debug_default="no"]) + + m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))]) + m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))]) + + AC_ARG_ENABLE(debug, + [AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])], + [],enable_debug=$ax_enable_debug_default) + + # empty mean debug yes + AS_IF([test "x$enable_debug" = "x"], + [enable_debug="yes"]) + + # case of debug + AS_CASE([$enable_debug], + [yes],[ + AC_MSG_RESULT(yes) + CFLAGS="${CFLAGS} -g -O0" + CXXFLAGS="${CXXFLAGS} -g -O0" + FFLAGS="${FFLAGS} -g -O0" + FCFLAGS="${FCFLAGS} -g -O0" + OBJCFLAGS="${OBJCFLAGS} -g -O0" + ], + [info],[ + AC_MSG_RESULT(info) + CFLAGS="${CFLAGS} -g" + CXXFLAGS="${CXXFLAGS} -g" + FFLAGS="${FFLAGS} -g" + FCFLAGS="${FCFLAGS} -g" + OBJCFLAGS="${OBJCFLAGS} -g" + ], + [profile],[ + AC_MSG_RESULT(profile) + CFLAGS="${CFLAGS} -g -pg" + CXXFLAGS="${CXXFLAGS} -g -pg" + FFLAGS="${FFLAGS} -g -pg" + FCFLAGS="${FCFLAGS} -g -pg" + OBJCFLAGS="${OBJCFLAGS} -g -pg" + LDFLAGS="${LDFLAGS} -pg" + ], + [ + AC_MSG_RESULT(no) + dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags + dnl by setting any unset environment flag variables + AS_IF([test "x${CFLAGS+set}" != "xset"], + [CFLAGS=""]) + AS_IF([test "x${CXXFLAGS+set}" != "xset"], + [CXXFLAGS=""]) + AS_IF([test "x${FFLAGS+set}" != "xset"], + [FFLAGS=""]) + AS_IF([test "x${FCFLAGS+set}" != "xset"], + [FCFLAGS=""]) + AS_IF([test "x${OBJCFLAGS+set}" != "xset"], + [OBJCFLAGS=""]) + ]) + + dnl Define various variables if debugging is disabled. + dnl assert.h is a NOP if NDEBUG is defined, so define it by default. + AS_IF([test "x$enable_debug" = "xyes"], + [m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])], + [m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])]) + ax_enable_debug=$enable_debug +]) diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4 new file mode 100644 index 00000000..eb01a6ce --- /dev/null +++ b/m4/ax_check_link_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the linker or gives an error. +# (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the linker's default flags +# when the check is done. The check is thus made with the flags: "LDFLAGS +# EXTRA-FLAGS FLAG". This can for example be used to force the linker to +# issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_LINK_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 4 + +AC_DEFUN([AX_CHECK_LINK_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl +AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ + ax_check_save_flags=$LDFLAGS + LDFLAGS="$LDFLAGS $4 $1" + AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + LDFLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_LINK_FLAGS diff --git a/m4/ax_compiler_flags.m4 b/m4/ax_compiler_flags.m4 new file mode 100644 index 00000000..6cfe398d --- /dev/null +++ b/m4/ax_compiler_flags.m4 @@ -0,0 +1,158 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compiler_flags.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_FLAGS([CFLAGS-VARIABLE], [LDFLAGS-VARIABLE], [IS-RELEASE], [EXTRA-BASE-CFLAGS], [EXTRA-YES-CFLAGS], [UNUSED], [UNUSED], [UNUSED], [EXTRA-BASE-LDFLAGS], [EXTRA-YES-LDFLAGS], [UNUSED], [UNUSED], [UNUSED]) +# +# DESCRIPTION +# +# Check for the presence of an --enable-compile-warnings option to +# configure, defaulting to "error" in normal operation, or "yes" if +# IS-RELEASE is equal to "yes". Return the value in the variable +# $ax_enable_compile_warnings. +# +# Depending on the value of --enable-compile-warnings, different compiler +# warnings are checked to see if they work with the current compiler and, +# if so, are appended to CFLAGS-VARIABLE and LDFLAGS-VARIABLE. This +# allows a consistent set of baseline compiler warnings to be used across +# a code base, irrespective of any warnings enabled locally by individual +# developers. By standardising the warnings used by all developers of a +# project, the project can commit to a zero-warnings policy, using -Werror +# to prevent compilation if new warnings are introduced. This makes +# catching bugs which are flagged by warnings a lot easier. +# +# By providing a consistent --enable-compile-warnings argument across all +# projects using this macro, continuous integration systems can easily be +# configured the same for all projects. Automated systems or build +# systems aimed at beginners may want to pass the --disable-Werror +# argument to unconditionally prevent warnings being fatal. +# +# --enable-compile-warnings can take the values: +# +# * no: Base compiler warnings only; not even -Wall. +# * yes: The above, plus a broad range of useful warnings. +# * error: The above, plus -Werror so that all warnings are fatal. +# Use --disable-Werror to override this and disable fatal +# warnings. +# +# The set of base and enabled flags can be augmented using the +# EXTRA-*-CFLAGS and EXTRA-*-LDFLAGS variables, which are tested and +# appended to the output variable if --enable-compile-warnings is not +# "no". Flags should not be disabled using these arguments, as the entire +# point of AX_COMPILER_FLAGS is to enforce a consistent set of useful +# compiler warnings on code, using warnings which have been chosen for low +# false positive rates. If a compiler emits false positives for a +# warning, a #pragma should be used in the code to disable the warning +# locally. See: +# +# https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas +# +# The EXTRA-* variables should only be used to supply extra warning flags, +# and not general purpose compiler flags, as they are controlled by +# configure options such as --disable-Werror. +# +# IS-RELEASE can be used to disable -Werror when making a release, which +# is useful for those hairy moments when you just want to get the release +# done as quickly as possible. Set it to "yes" to disable -Werror. By +# default, it uses the value of $ax_is_release, so if you are using the +# AX_IS_RELEASE macro, there is no need to pass this parameter. For +# example: +# +# AX_IS_RELEASE([git-directory]) +# AX_COMPILER_FLAGS() +# +# CFLAGS-VARIABLE defaults to WARN_CFLAGS, and LDFLAGS-VARIABLE defaults +# to WARN_LDFLAGS. Both variables are AC_SUBST-ed by this macro, but must +# be manually added to the CFLAGS and LDFLAGS variables for each target in +# the code base. +# +# If C++ language support is enabled with AC_PROG_CXX, which must occur +# before this macro in configure.ac, warning flags for the C++ compiler +# are AC_SUBST-ed as WARN_CXXFLAGS, and must be manually added to the +# CXXFLAGS variables for each target in the code base. EXTRA-*-CFLAGS can +# be used to augment the base and enabled flags. +# +# Warning flags for g-ir-scanner (from GObject Introspection) are +# AC_SUBST-ed as WARN_SCANNERFLAGS. This variable must be manually added +# to the SCANNERFLAGS variable for each GIR target in the code base. If +# extra g-ir-scanner flags need to be enabled, the AX_COMPILER_FLAGS_GIR +# macro must be invoked manually. +# +# AX_COMPILER_FLAGS may add support for other tools in future, in addition +# to the compiler and linker. No extra EXTRA-* variables will be added +# for those tools, and all extra support will still use the single +# --enable-compile-warnings configure option. For finer grained control +# over the flags for individual tools, use AX_COMPILER_FLAGS_CFLAGS, +# AX_COMPILER_FLAGS_LDFLAGS and AX_COMPILER_FLAGS_* for new tools. +# +# The UNUSED variables date from a previous version of this macro, and are +# automatically appended to the preceding non-UNUSED variable. They should +# be left empty in new uses of the macro. +# +# LICENSE +# +# Copyright (c) 2014, 2015 Philip Withnall +# Copyright (c) 2015 David King +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 13 + +# _AX_COMPILER_FLAGS_LANG([LANGNAME]) +m4_defun([_AX_COMPILER_FLAGS_LANG], +[m4_ifdef([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [], + [m4_define([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [])dnl + AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_]$1[FLAGS])])dnl +]) + +AC_DEFUN([AX_COMPILER_FLAGS],[ + # C support is enabled by default. + _AX_COMPILER_FLAGS_LANG([C]) + # Only enable C++ support if AC_PROG_CXX is called. The redefinition of + # AC_PROG_CXX is so that a fatal error is emitted if this macro is called + # before AC_PROG_CXX, which would otherwise cause no C++ warnings to be + # checked. + AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AX_COMPILER_FLAGS_LANG([CXX])], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[_AX_COMPILER_FLAGS_LANG([CXX])])]) + AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_LDFLAGS]) + + # Default value for IS-RELEASE is $ax_is_release + ax_compiler_flags_is_release=m4_tolower(m4_normalize(ifelse([$3],, + [$ax_is_release], + [$3]))) + + AC_ARG_ENABLE([compile-warnings], + AS_HELP_STRING([--enable-compile-warnings=@<:@no/yes/error@:>@], + [Enable compiler warnings and errors]),, + [AS_IF([test "$ax_compiler_flags_is_release" = "yes"], + [enable_compile_warnings="yes"], + [enable_compile_warnings="error"])]) + AC_ARG_ENABLE([Werror], + AS_HELP_STRING([--disable-Werror], + [Unconditionally make all compiler warnings non-fatal]),, + [enable_Werror=maybe]) + + # Return the user's chosen warning level + AS_IF([test "$enable_Werror" = "no" -a \ + "$enable_compile_warnings" = "error"],[ + enable_compile_warnings="yes" + ]) + + ax_enable_compile_warnings=$enable_compile_warnings + + AX_COMPILER_FLAGS_CFLAGS([$1],[$ax_compiler_flags_is_release], + [$4],[$5 $6 $7 $8]) + m4_ifdef([_AX_COMPILER_FLAGS_LANG_CXX_enabled], + [AX_COMPILER_FLAGS_CXXFLAGS([WARN_CXXFLAGS], + [$ax_compiler_flags_is_release], + [$4],[$5 $6 $7 $8])]) + AX_COMPILER_FLAGS_LDFLAGS([$2],[$ax_compiler_flags_is_release], + [$9],[$10 $11 $12 $13]) + AX_COMPILER_FLAGS_GIR([WARN_SCANNERFLAGS],[$ax_compiler_flags_is_release]) +])dnl AX_COMPILER_FLAGS diff --git a/m4/ax_compiler_flags_cflags.m4 b/m4/ax_compiler_flags_cflags.m4 new file mode 100644 index 00000000..f470f8f7 --- /dev/null +++ b/m4/ax_compiler_flags_cflags.m4 @@ -0,0 +1,133 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_compiler_flags_cflags.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_COMPILER_FLAGS_CFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS]) +# +# DESCRIPTION +# +# Add warning flags for the C compiler to VARIABLE, which defaults to +# WARN_CFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be +# manually added to the CFLAGS variable for each target in the code base. +# +# This macro depends on the environment set up by AX_COMPILER_FLAGS. +# Specifically, it uses the value of $ax_enable_compile_warnings to decide +# which flags to enable. +# +# LICENSE +# +# Copyright (c) 2014, 2015 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +AC_DEFUN([AX_COMPILER_FLAGS_CFLAGS],[ + AC_REQUIRE([AC_PROG_SED]) + AX_REQUIRE_DEFINED([AX_APPEND_COMPILE_FLAGS]) + AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) + AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) + + # Variable names + m4_define(ax_warn_cflags_variable, + [m4_normalize(ifelse([$1],,[WARN_CFLAGS],[$1]))]) + + AC_LANG_PUSH([C]) + + # Always pass -Werror=unknown-warning-option to get Clang to fail on bad + # flags, otherwise they are always appended to the warn_cflags variable, and + # Clang warns on them for every compilation unit. + # If this is passed to GCC, it will explode, so the flag must be enabled + # conditionally. + AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[ + ax_compiler_flags_test="-Werror=unknown-warning-option" + ],[ + ax_compiler_flags_test="" + ]) + + # Base flags + AX_APPEND_COMPILE_FLAGS([ dnl + -fno-strict-aliasing dnl + $3 dnl + ],ax_warn_cflags_variable,[$ax_compiler_flags_test]) + + AS_IF([test "$ax_enable_compile_warnings" != "no"],[ + # "yes" flags + AX_APPEND_COMPILE_FLAGS([ dnl + -Wall dnl + -Wextra dnl + -Wundef dnl + -Wnested-externs dnl + -Wwrite-strings dnl + -Wpointer-arith dnl + -Wmissing-declarations dnl + -Wmissing-prototypes dnl + -Wstrict-prototypes dnl + -Wredundant-decls dnl + -Wno-unused-parameter dnl + -Wno-missing-field-initializers dnl + -Wdeclaration-after-statement dnl + -Wformat=2 dnl + -Wold-style-definition dnl + -Wcast-align dnl + -Wformat-nonliteral dnl + -Wformat-security dnl + -Wsign-compare dnl + -Wstrict-aliasing dnl + -Wshadow dnl + -Winline dnl + -Wpacked dnl + -Wmissing-format-attribute dnl + -Wmissing-noreturn dnl + -Winit-self dnl + -Wredundant-decls dnl + -Wmissing-include-dirs dnl + -Wunused-but-set-variable dnl + -Warray-bounds dnl + -Wimplicit-function-declaration dnl + -Wreturn-type dnl + -Wswitch-enum dnl + -Wswitch-default dnl + $4 dnl + $5 dnl + $6 dnl + $7 dnl + ],ax_warn_cflags_variable,[$ax_compiler_flags_test]) + ]) + AS_IF([test "$ax_enable_compile_warnings" = "error"],[ + # "error" flags; -Werror has to be appended unconditionally because + # it's not possible to test for + # + # suggest-attribute=format is disabled because it gives too many false + # positives + AX_APPEND_FLAG([-Werror],ax_warn_cflags_variable) + + AX_APPEND_COMPILE_FLAGS([ dnl + -Wno-suggest-attribute=format dnl + ],ax_warn_cflags_variable,[$ax_compiler_flags_test]) + ]) + + # In the flags below, when disabling specific flags, always add *both* + # -Wno-foo and -Wno-error=foo. This fixes the situation where (for example) + # we enable -Werror, disable a flag, and a build bot passes CFLAGS=-Wall, + # which effectively turns that flag back on again as an error. + for flag in $ax_warn_cflags_variable; do + AS_CASE([$flag], + [-Wno-*=*],[], + [-Wno-*],[ + AX_APPEND_COMPILE_FLAGS([-Wno-error=$(AS_ECHO([$flag]) | $SED 's/^-Wno-//')], + ax_warn_cflags_variable, + [$ax_compiler_flags_test]) + ]) + done + + AC_LANG_POP([C]) + + # Substitute the variables + AC_SUBST(ax_warn_cflags_variable) +])dnl AX_COMPILER_FLAGS diff --git a/m4/ax_compiler_flags_cxxflags.m4 b/m4/ax_compiler_flags_cxxflags.m4 new file mode 100644 index 00000000..2d875e85 --- /dev/null +++ b/m4/ax_compiler_flags_cxxflags.m4 @@ -0,0 +1,129 @@ +# ============================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compiler_flags_cxxflags.html +# ============================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_FLAGS_CXXFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS]) +# +# DESCRIPTION +# +# Add warning flags for the C++ compiler to VARIABLE, which defaults to +# WARN_CXXFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be +# manually added to the CXXFLAGS variable for each target in the code +# base. +# +# This macro depends on the environment set up by AX_COMPILER_FLAGS. +# Specifically, it uses the value of $ax_enable_compile_warnings to decide +# which flags to enable. +# +# LICENSE +# +# Copyright (c) 2015 David King +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 7 + +AC_DEFUN([AX_COMPILER_FLAGS_CXXFLAGS],[ + AC_REQUIRE([AC_PROG_SED]) + AX_REQUIRE_DEFINED([AX_APPEND_COMPILE_FLAGS]) + AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) + AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) + + # Variable names + m4_define(ax_warn_cxxflags_variable, + [m4_normalize(ifelse([$1],,[WARN_CXXFLAGS],[$1]))]) + + AC_LANG_PUSH([C++]) + + # Always pass -Werror=unknown-warning-option to get Clang to fail on bad + # flags, otherwise they are always appended to the warn_cxxflags variable, + # and Clang warns on them for every compilation unit. + # If this is passed to GCC, it will explode, so the flag must be enabled + # conditionally. + AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[ + ax_compiler_flags_test="-Werror=unknown-warning-option" + ],[ + ax_compiler_flags_test="" + ]) + + # Base flags + AX_APPEND_COMPILE_FLAGS([ dnl + -fno-strict-aliasing dnl + $3 dnl + ],ax_warn_cxxflags_variable,[$ax_compiler_flags_test]) + + AS_IF([test "$ax_enable_compile_warnings" != "no"],[ + # "yes" flags + AX_APPEND_COMPILE_FLAGS([ dnl + -Wall dnl + -Wextra dnl + -Wundef dnl + -Wwrite-strings dnl + -Wpointer-arith dnl + -Wmissing-declarations dnl + -Wredundant-decls dnl + -Wno-unused-parameter dnl + -Wno-missing-field-initializers dnl + -Wformat=2 dnl + -Wcast-align dnl + -Wformat-nonliteral dnl + -Wformat-security dnl + -Wsign-compare dnl + -Wstrict-aliasing dnl + -Wshadow dnl + -Winline dnl + -Wpacked dnl + -Wmissing-format-attribute dnl + -Wmissing-noreturn dnl + -Winit-self dnl + -Wredundant-decls dnl + -Wmissing-include-dirs dnl + -Wunused-but-set-variable dnl + -Warray-bounds dnl + -Wreturn-type dnl + -Wno-overloaded-virtual dnl + -Wswitch-enum dnl + -Wswitch-default dnl + $4 dnl + $5 dnl + $6 dnl + $7 dnl + ],ax_warn_cxxflags_variable,[$ax_compiler_flags_test]) + ]) + AS_IF([test "$ax_enable_compile_warnings" = "error"],[ + # "error" flags; -Werror has to be appended unconditionally because + # it's not possible to test for + # + # suggest-attribute=format is disabled because it gives too many false + # positives + AX_APPEND_FLAG([-Werror],ax_warn_cxxflags_variable) + + AX_APPEND_COMPILE_FLAGS([ dnl + -Wno-suggest-attribute=format dnl + ],ax_warn_cxxflags_variable,[$ax_compiler_flags_test]) + ]) + + # In the flags below, when disabling specific flags, always add *both* + # -Wno-foo and -Wno-error=foo. This fixes the situation where (for example) + # we enable -Werror, disable a flag, and a build bot passes CXXFLAGS=-Wall, + # which effectively turns that flag back on again as an error. + for flag in $ax_warn_cxxflags_variable; do + AS_CASE([$flag], + [-Wno-*=*],[], + [-Wno-*],[ + AX_APPEND_COMPILE_FLAGS([-Wno-error=$(AS_ECHO([$flag]) | $SED 's/^-Wno-//')], + ax_warn_cxxflags_variable, + [$ax_compiler_flags_test]) + ]) + done + + AC_LANG_POP([C++]) + + # Substitute the variables + AC_SUBST(ax_warn_cxxflags_variable) +])dnl AX_COMPILER_FLAGS_CXXFLAGS diff --git a/m4/ax_compiler_flags_gir.m4 b/m4/ax_compiler_flags_gir.m4 new file mode 100644 index 00000000..180f50d4 --- /dev/null +++ b/m4/ax_compiler_flags_gir.m4 @@ -0,0 +1,60 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compiler_flags_gir.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_FLAGS_GIR([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS]) +# +# DESCRIPTION +# +# Add warning flags for the g-ir-scanner (from GObject Introspection) to +# VARIABLE, which defaults to WARN_SCANNERFLAGS. VARIABLE is AC_SUBST-ed +# by this macro, but must be manually added to the SCANNERFLAGS variable +# for each GIR target in the code base. +# +# This macro depends on the environment set up by AX_COMPILER_FLAGS. +# Specifically, it uses the value of $ax_enable_compile_warnings to decide +# which flags to enable. +# +# LICENSE +# +# Copyright (c) 2015 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 4 + +AC_DEFUN([AX_COMPILER_FLAGS_GIR],[ + AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) + + # Variable names + m4_define(ax_warn_scannerflags_variable, + [m4_normalize(ifelse([$1],,[WARN_SCANNERFLAGS],[$1]))]) + + # Base flags + AX_APPEND_FLAG([$3],ax_warn_scannerflags_variable) + + AS_IF([test "$ax_enable_compile_warnings" != "no"],[ + # "yes" flags + AX_APPEND_FLAG([ dnl + --warn-all dnl + $4 dnl + $5 dnl + $6 dnl + $7 dnl + ],ax_warn_scannerflags_variable) + ]) + AS_IF([test "$ax_enable_compile_warnings" = "error"],[ + # "error" flags + AX_APPEND_FLAG([ dnl + --warn-error dnl + ],ax_warn_scannerflags_variable) + ]) + + # Substitute the variables + AC_SUBST(ax_warn_scannerflags_variable) +])dnl AX_COMPILER_FLAGS diff --git a/m4/ax_compiler_flags_ldflags.m4 b/m4/ax_compiler_flags_ldflags.m4 new file mode 100644 index 00000000..228471ec --- /dev/null +++ b/m4/ax_compiler_flags_ldflags.m4 @@ -0,0 +1,75 @@ +# ============================================================================= +# http://www.gnu.org/software/autoconf-archive/ax_compiler_flags_ldflags.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_COMPILER_FLAGS_LDFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS]) +# +# DESCRIPTION +# +# Add warning flags for the linker to VARIABLE, which defaults to +# WARN_LDFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be +# manually added to the LDFLAGS variable for each target in the code base. +# +# This macro depends on the environment set up by AX_COMPILER_FLAGS. +# Specifically, it uses the value of $ax_enable_compile_warnings to decide +# which flags to enable. +# +# LICENSE +# +# Copyright (c) 2014, 2015 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 5 + +AC_DEFUN([AX_COMPILER_FLAGS_LDFLAGS],[ + AX_REQUIRE_DEFINED([AX_APPEND_LINK_FLAGS]) + AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) + AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) + + # Variable names + m4_define(ax_warn_ldflags_variable, + [m4_normalize(ifelse([$1],,[WARN_LDFLAGS],[$1]))]) + + # Always pass -Werror=unknown-warning-option to get Clang to fail on bad + # flags, otherwise they are always appended to the warn_ldflags variable, + # and Clang warns on them for every compilation unit. + # If this is passed to GCC, it will explode, so the flag must be enabled + # conditionally. + AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[ + ax_compiler_flags_test="-Werror=unknown-warning-option" + ],[ + ax_compiler_flags_test="" + ]) + + # Base flags + AX_APPEND_LINK_FLAGS([ dnl + -Wl,--no-as-needed dnl + $3 dnl + ],ax_warn_ldflags_variable,[$ax_compiler_flags_test]) + + AS_IF([test "$ax_enable_compile_warnings" != "no"],[ + # "yes" flags + AX_APPEND_LINK_FLAGS([$4 $5 $6 $7], + ax_warn_ldflags_variable, + [$ax_compiler_flags_test]) + ]) + AS_IF([test "$ax_enable_compile_warnings" = "error"],[ + # "error" flags; -Werror has to be appended unconditionally because + # it's not possible to test for + # + # suggest-attribute=format is disabled because it gives too many false + # positives + AX_APPEND_LINK_FLAGS([ dnl + -Wl,--fatal-warnings dnl + ],ax_warn_ldflags_variable,[$ax_compiler_flags_test]) + ]) + + # Substitute the variables + AC_SUBST(ax_warn_ldflags_variable) +])dnl AX_COMPILER_FLAGS diff --git a/m4/ax_compiler_vendor.m4 b/m4/ax_compiler_vendor.m4 new file mode 100644 index 00000000..39ca3c0f --- /dev/null +++ b/m4/ax_compiler_vendor.m4 @@ -0,0 +1,87 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_VENDOR +# +# DESCRIPTION +# +# Determine the vendor of the C/C++ compiler, e.g., gnu, intel, ibm, sun, +# hp, borland, comeau, dec, cray, kai, lcc, metrowerks, sgi, microsoft, +# watcom, etc. The vendor is returned in the cache variable +# $ax_cv_c_compiler_vendor for C and $ax_cv_cxx_compiler_vendor for C++. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 15 + +AC_DEFUN([AX_COMPILER_VENDOR], +[AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, + dnl Please add if possible support to ax_compiler_version.m4 + [# note: don't check for gcc first since some other compilers define __GNUC__ + vendors="intel: __ICC,__ECC,__INTEL_COMPILER + ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__ + pathscale: __PATHCC__,__PATHSCALE__ + clang: __clang__ + cray: _CRAYC + fujitsu: __FUJITSU + gnu: __GNUC__ + sun: __SUNPRO_C,__SUNPRO_CC + hp: __HP_cc,__HP_aCC + dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER + borland: __BORLANDC__,__CODEGEARC__,__TURBOC__ + comeau: __COMO__ + kai: __KCC + lcc: __LCC__ + sgi: __sgi,sgi + microsoft: _MSC_VER + metrowerks: __MWERKS__ + watcom: __WATCOMC__ + portland: __PGI + tcc: __TINYC__ + unknown: UNKNOWN" + for ventest in $vendors; do + case $ventest in + *:) vendor=$ventest; continue ;; + *) vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" ;; + esac + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ + #if !($vencpp) + thisisanerror; + #endif + ])], [break]) + done + ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1` + ]) +]) diff --git a/m4/ax_cxx_compile_stdcxx_0x.m4 b/m4/ax_cxx_compile_stdcxx_0x.m4 new file mode 100644 index 00000000..52c39849 --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_0x.m4 @@ -0,0 +1,111 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_0x.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_0X +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++0x +# standard. +# +# This macro is deprecated and has been superseded by the +# AX_CXX_COMPILE_STDCXX_11 macro which should be used instead. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AU_ALIAS([AC_CXX_COMPILE_STDCXX_0X], [AX_CXX_COMPILE_STDCXX_0X]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_0X], [ + AC_OBSOLETE([$0], [; use AX_CXX_COMPILE_STDCXX_11 instead]) + AC_CACHE_CHECK(if g++ supports C++0x features without additional flags, + ax_cv_cxx_compile_cxx0x_native, + [AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c);],, + ax_cv_cxx_compile_cxx0x_native=yes, ax_cv_cxx_compile_cxx0x_native=no) + AC_LANG_RESTORE + ]) + + AC_CACHE_CHECK(if g++ supports C++0x features with -std=c++0x, + ax_cv_cxx_compile_cxx0x_cxx, + [AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_CXX="$CXX" + CXX="$CXX -std=c++0x" + AC_TRY_COMPILE([ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c);],, + ax_cv_cxx_compile_cxx0x_cxx=yes, ax_cv_cxx_compile_cxx0x_cxx=no) + CXX="$ac_save_CXX" + AC_LANG_RESTORE + ]) + + AC_CACHE_CHECK(if g++ supports C++0x features with -std=gnu++0x, + ax_cv_cxx_compile_cxx0x_gxx, + [AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_CXX="$CXX" + CXX="$CXX -std=gnu++0x" + AC_TRY_COMPILE([ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c);],, + ax_cv_cxx_compile_cxx0x_gxx=yes, ax_cv_cxx_compile_cxx0x_gxx=no) + CXX="$ac_save_CXX" + AC_LANG_RESTORE + ]) + + if test "$ax_cv_cxx_compile_cxx0x_native" = yes || + test "$ax_cv_cxx_compile_cxx0x_cxx" = yes || + test "$ax_cv_cxx_compile_cxx0x_gxx" = yes; then + AC_DEFINE(HAVE_STDCXX_0X,,[Define if g++ supports C++0x features. ]) + fi +]) diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 00000000..516da37e --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,172 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXXFLAGS to enable support. +# +# The first argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The second argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline C++11 support is required and that the macro +# should error out if no mode with that support is found. If specified +# 'optional', then configuration proceeds regardless, after defining +# HAVE_CXX11 if and only if a supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 13 + +m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() override {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + auto l = [](){}; + // Prevent Clang error: unused variable 'l' [-Werror,-Wunused-variable] + struct use_l { use_l() { l(); } }; + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this + namespace test_template_alias_sfinae { + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { + func(0); + } + } + + // Check for C++11 attribute support + void noret [[noreturn]] () { throw 0; } +]]) + +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl + m4_if([$1], [], [], + [$1], [ext], [], + [$1], [noext], [], + [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl + m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], + [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], + [$2], [optional], [ax_cxx_compile_cxx11_required=false], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++11 features by default, + ax_cv_cxx_compile_cxx11, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [ax_cv_cxx_compile_cxx11=yes], + [ax_cv_cxx_compile_cxx11=no])]) + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + m4_if([$1], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++11 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + + m4_if([$1], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for switch in -std=c++11 -std=c++0x +std=c++11 "-h std=c++11"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + AC_MSG_NOTICE([No compiler with C++11 support was found]) + else + HAVE_CXX11=1 + AC_DEFINE(HAVE_CXX11,1, + [define if the compiler supports basic C++11 syntax]) + fi + + AC_SUBST(HAVE_CXX11) + fi +]) diff --git a/m4/ax_generate_changelog.m4 b/m4/ax_generate_changelog.m4 new file mode 100644 index 00000000..d9d5cd10 --- /dev/null +++ b/m4/ax_generate_changelog.m4 @@ -0,0 +1,99 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_generate_changelog.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GENERATE_CHANGELOG() +# +# DESCRIPTION +# +# Builds a rule for generating a ChangeLog file from version control +# system commit messages. Currently, the only supported VCS is git, but +# support for others could be added in future. +# +# Defines GENERATE_CHANGELOG_RULES which should be substituted in your +# Makefile. +# +# Usage example: +# +# configure.ac: +# +# AX_GENERATE_CHANGELOG +# +# Makefile.am: +# +# @GENERATE_CHANGELOG_RULES@ +# CHANGELOG_START = 0.2.3^ +# dist-hook: dist-ChangeLog +# +# ChangeLog (stub committed to VCS): +# +# The ChangeLog is auto-generated when releasing. +# If you are seeing this, use 'git log' for a detailed list of changes. +# +# This results in a "dist-ChangeLog" rule being added to the Makefile. +# When run, "dist-ChangeLog" will generate a ChangeLog in the +# $(top_distdir), using $(CHANGELOG_GIT_FLAGS) to format the output from +# "git log" being run in $(CHANGELOG_GIT_DIR). +# +# Unless Automake is initialised with the 'foreign' option, a dummy +# ChangeLog file must be committed to VCS in $(top_srcdir), containing the +# text above (for example). It will be substituted by the automatically +# generated ChangeLog during "make dist". +# +# LICENSE +# +# Copyright (c) 2015 David King +# Copyright (c) 2015 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +AC_DEFUN([AX_GENERATE_CHANGELOG],[ + # Find git, defaulting to the 'missing' script so the user gets a nice + # message if git is missing, rather than a plain 'command not found'. + AC_PATH_PROG([GIT],[git],[${am_missing_run}git]) + AC_SUBST([GIT]) + + # Build the ChangeLog rules. + m4_pattern_allow([AM_V_GEN]) +GENERATE_CHANGELOG_RULES=' +# Generate ChangeLog +# +# Optional: +# - CHANGELOG_START: git commit ID or tag name to output changelogs from +# (exclusive). (Default: include all commits) +# - CHANGELOG_GIT_FLAGS: General flags to pass to git-log when generating the +# ChangeLog. (Default: various) +# - CHANGELOG_GIT_DIR: .git directory to use. (Default: $(top_srcdir)/.git) + +# git-specific +CHANGELOG_GIT_FLAGS ?= --stat -M -C --name-status --no-color +CHANGELOG_GIT_DIR ?= $(top_srcdir)/.git + +ifeq ($(CHANGELOG_START),) +CHANGELOG_GIT_RANGE = +else +CHANGELOG_GIT_RANGE = $(CHANGELOG_START).. +endif + +# Generate a ChangeLog in $(top_distdir) +dist-ChangeLog: + $(AM_V_GEN)if $(GIT) \ + --git-dir=$(CHANGELOG_GIT_DIR) --work-tree=$(top_srcdir) log \ + $(CHANGELOG_GIT_FLAGS) $(CHANGELOG_GIT_RANGE) \ + | fmt --split-only >.ChangeLog.tmp; \ + then mv -f .ChangeLog.tmp "$(top_distdir)/ChangeLog"; \ + else rm -f .ChangeLog.tmp; exit 1; fi + +.PHONY: dist-ChangeLog +' + + AC_SUBST([GENERATE_CHANGELOG_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([GENERATE_CHANGELOG_RULES])]) +]) diff --git a/m4/ax_is_release.m4 b/m4/ax_is_release.m4 new file mode 100644 index 00000000..9ec67469 --- /dev/null +++ b/m4/ax_is_release.m4 @@ -0,0 +1,69 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_is_release.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_IS_RELEASE(POLICY) +# +# DESCRIPTION +# +# Determine whether the code is being configured as a release, or from +# git. Set the ax_is_release variable to 'yes' or 'no'. +# +# If building a release version, it is recommended that the configure +# script disable compiler errors and debug features, by conditionalising +# them on the ax_is_release variable. If building from git, these +# features should be enabled. +# +# The POLICY parameter specifies how ax_is_release is determined. It can +# take the following values: +# +# * git-directory: ax_is_release will be 'no' if a '.git' directory exists +# * minor-version: ax_is_release will be 'no' if the minor version number +# in $PACKAGE_VERSION is odd; this assumes +# $PACKAGE_VERSION follows the 'major.minor.micro' scheme +# * micro-version: ax_is_release will be 'no' if the micro version number +# in $PACKAGE_VERSION is odd; this assumes +# $PACKAGE_VERSION follows the 'major.minor.micro' scheme +# * always: ax_is_release will always be 'yes' +# * never: ax_is_release will always be 'no' +# +# Other policies may be added in future. +# +# LICENSE +# +# Copyright (c) 2015 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. + +#serial 3 + +AC_DEFUN([AX_IS_RELEASE],[ + AC_BEFORE([AC_INIT],[$0]) + + m4_case([$1], + [git-directory],[ + # $is_release = (.git directory does not exist) + AS_IF([test -d .git],[ax_is_release=no],[ax_is_release=yes]) + ], + [minor-version],[ + # $is_release = ($minor_version is even) + minor_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]][[^.]]*.\([[^.]][[^.]]*\).*/\1/'` + AS_IF([test "$(( $minor_version % 2 ))" -ne 0], + [ax_is_release=no],[ax_is_release=yes]) + ], + [micro-version],[ + # $is_release = ($micro_version is even) + micro_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]]*\.[[^.]]*\.\([[^.]]*\).*/\1/'` + AS_IF([test "$(( $micro_version % 2 ))" -ne 0], + [ax_is_release=no],[ax_is_release=yes]) + ], + [always],[ax_is_release=yes], + [never],[ax_is_release=no], + [ + AC_MSG_ERROR([Invalid policy. Valid policies: git-directory, minor-version.]) + ]) +]) diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4 new file mode 100644 index 00000000..cae11112 --- /dev/null +++ b/m4/ax_require_defined.m4 @@ -0,0 +1,37 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_require_defined.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_REQUIRE_DEFINED(MACRO) +# +# DESCRIPTION +# +# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have +# been defined and thus are available for use. This avoids random issues +# where a macro isn't expanded. Instead the configure script emits a +# non-fatal: +# +# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found +# +# It's like AC_REQUIRE except it doesn't expand the required macro. +# +# Here's an example: +# +# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) +# +# LICENSE +# +# Copyright (c) 2014 Mike Frysinger +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +AC_DEFUN([AX_REQUIRE_DEFINED], [dnl + m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) +])dnl AX_REQUIRE_DEFINED diff --git a/m4/codeset.m4 b/m4/codeset.m4 new file mode 100644 index 00000000..d7de8d67 --- /dev/null +++ b/m4/codeset.m4 @@ -0,0 +1,23 @@ +# codeset.m4 serial 5 (gettext-0.18.2) +dnl Copyright (C) 2000-2002, 2006, 2008-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([AM_LANGINFO_CODESET], +[ + AC_CACHE_CHECK([for nl_langinfo and CODESET], [am_cv_langinfo_codeset], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[char* cs = nl_langinfo(CODESET); return !cs;]])], + [am_cv_langinfo_codeset=yes], + [am_cv_langinfo_codeset=no]) + ]) + if test $am_cv_langinfo_codeset = yes; then + AC_DEFINE([HAVE_LANGINFO_CODESET], [1], + [Define if you have and nl_langinfo(CODESET).]) + fi +]) diff --git a/m4/extern-inline.m4 b/m4/extern-inline.m4 new file mode 100644 index 00000000..72800650 --- /dev/null +++ b/m4/extern-inline.m4 @@ -0,0 +1,101 @@ +dnl 'extern inline' a la ISO C99. + +dnl Copyright 2012-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_EXTERN_INLINE], +[ + AH_VERBATIM([extern_inline], +[/* Please see the Gnulib manual for how to use these macros. + + Suppress extern inline with HP-UX cc, as it appears to be broken; see + . + + Suppress extern inline with Sun C in standards-conformance mode, as it + mishandles inline functions that call each other. E.g., for 'inline void f + (void) { } inline void g (void) { f (); }', c99 incorrectly complains + 'reference to static identifier "f" in extern inline function'. + This bug was observed with Sun C 5.12 SunOS_i386 2011/11/16. + + Suppress extern inline (with or without __attribute__ ((__gnu_inline__))) + on configurations that mistakenly use 'static inline' to implement + functions or macros in standard C headers like . For example, + if isdigit is mistakenly implemented via a static inline function, + a program containing an extern inline function that calls isdigit + may not work since the C standard prohibits extern inline functions + from calling static functions. This bug is known to occur on: + + OS X 10.8 and earlier; see: + http://lists.gnu.org/archive/html/bug-gnulib/2012-12/msg00023.html + + DragonFly; see + http://muscles.dragonflybsd.org/bulk/bleeding-edge-potential/latest-per-pkg/ah-tty-0.3.12.log + + FreeBSD; see: + http://lists.gnu.org/archive/html/bug-gnulib/2014-07/msg00104.html + + OS X 10.9 has a macro __header_inline indicating the bug is fixed for C and + for clang but remains for g++; see . + Assume DragonFly and FreeBSD will be similar. */ +#if (((defined __APPLE__ && defined __MACH__) \ + || defined __DragonFly__ || defined __FreeBSD__) \ + && (defined __header_inline \ + ? (defined __cplusplus && defined __GNUC_STDC_INLINE__ \ + && ! defined __clang__) \ + : ((! defined _DONT_USE_CTYPE_INLINE_ \ + && (defined __GNUC__ || defined __cplusplus)) \ + || (defined _FORTIFY_SOURCE && 0 < _FORTIFY_SOURCE \ + && defined __GNUC__ && ! defined __cplusplus)))) +# define _GL_EXTERN_INLINE_STDHEADER_BUG +#endif +#if ((__GNUC__ \ + ? defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ \ + : (199901L <= __STDC_VERSION__ \ + && !defined __HP_cc \ + && !(defined __SUNPRO_C && __STDC__))) \ + && !defined _GL_EXTERN_INLINE_STDHEADER_BUG) +# define _GL_INLINE inline +# define _GL_EXTERN_INLINE extern inline +# define _GL_EXTERN_INLINE_IN_USE +#elif (2 < __GNUC__ + (7 <= __GNUC_MINOR__) && !defined __STRICT_ANSI__ \ + && !defined _GL_EXTERN_INLINE_STDHEADER_BUG) +# if defined __GNUC_GNU_INLINE__ && __GNUC_GNU_INLINE__ + /* __gnu_inline__ suppresses a GCC 4.2 diagnostic. */ +# define _GL_INLINE extern inline __attribute__ ((__gnu_inline__)) +# else +# define _GL_INLINE extern inline +# endif +# define _GL_EXTERN_INLINE extern +# define _GL_EXTERN_INLINE_IN_USE +#else +# define _GL_INLINE static _GL_UNUSED +# define _GL_EXTERN_INLINE static _GL_UNUSED +#endif + +/* In GCC 4.6 (inclusive) to 5.1 (exclusive), + suppress bogus "no previous prototype for 'FOO'" + and "no previous declaration for 'FOO'" diagnostics, + when FOO is an inline function in the header; see + and + . */ +#if __GNUC__ == 4 && 6 <= __GNUC_MINOR__ +# if defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ +# define _GL_INLINE_HEADER_CONST_PRAGMA +# else +# define _GL_INLINE_HEADER_CONST_PRAGMA \ + _Pragma ("GCC diagnostic ignored \"-Wsuggest-attribute=const\"") +# endif +# define _GL_INLINE_HEADER_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wmissing-prototypes\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmissing-declarations\"") \ + _GL_INLINE_HEADER_CONST_PRAGMA +# define _GL_INLINE_HEADER_END \ + _Pragma ("GCC diagnostic pop") +#else +# define _GL_INLINE_HEADER_BEGIN +# define _GL_INLINE_HEADER_END +#endif]) +]) diff --git a/m4/fcntl-o.m4 b/m4/fcntl-o.m4 new file mode 100644 index 00000000..891a62fb --- /dev/null +++ b/m4/fcntl-o.m4 @@ -0,0 +1,134 @@ +# fcntl-o.m4 serial 4 +dnl Copyright (C) 2006, 2009-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Written by Paul Eggert. + +# Test whether the flags O_NOATIME and O_NOFOLLOW actually work. +# Define HAVE_WORKING_O_NOATIME to 1 if O_NOATIME works, or to 0 otherwise. +# Define HAVE_WORKING_O_NOFOLLOW to 1 if O_NOFOLLOW works, or to 0 otherwise. +AC_DEFUN([gl_FCNTL_O_FLAGS], +[ + dnl Persuade glibc to define O_NOATIME and O_NOFOLLOW. + dnl AC_USE_SYSTEM_EXTENSIONS was introduced in autoconf 2.60 and obsoletes + dnl AC_GNU_SOURCE. + m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], + [AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])], + [AC_REQUIRE([AC_GNU_SOURCE])]) + + AC_CHECK_HEADERS_ONCE([unistd.h]) + AC_CHECK_FUNCS_ONCE([symlink]) + AC_CACHE_CHECK([for working fcntl.h], [gl_cv_header_working_fcntl_h], + [AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include + #if HAVE_UNISTD_H + # include + #else /* on Windows with MSVC */ + # include + # include + # defined sleep(n) _sleep ((n) * 1000) + #endif + #include + #ifndef O_NOATIME + #define O_NOATIME 0 + #endif + #ifndef O_NOFOLLOW + #define O_NOFOLLOW 0 + #endif + static int const constants[] = + { + O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC, O_APPEND, + O_NONBLOCK, O_SYNC, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY + }; + ]], + [[ + int result = !constants; + #if HAVE_SYMLINK + { + static char const sym[] = "conftest.sym"; + if (symlink ("/dev/null", sym) != 0) + result |= 2; + else + { + int fd = open (sym, O_WRONLY | O_NOFOLLOW | O_CREAT, 0); + if (fd >= 0) + { + close (fd); + result |= 4; + } + } + if (unlink (sym) != 0 || symlink (".", sym) != 0) + result |= 2; + else + { + int fd = open (sym, O_RDONLY | O_NOFOLLOW); + if (fd >= 0) + { + close (fd); + result |= 4; + } + } + unlink (sym); + } + #endif + { + static char const file[] = "confdefs.h"; + int fd = open (file, O_RDONLY | O_NOATIME); + if (fd < 0) + result |= 8; + else + { + struct stat st0; + if (fstat (fd, &st0) != 0) + result |= 16; + else + { + char c; + sleep (1); + if (read (fd, &c, 1) != 1) + result |= 24; + else + { + if (close (fd) != 0) + result |= 32; + else + { + struct stat st1; + if (stat (file, &st1) != 0) + result |= 40; + else + if (st0.st_atime != st1.st_atime) + result |= 64; + } + } + } + } + } + return result;]])], + [gl_cv_header_working_fcntl_h=yes], + [case $? in #( + 4) gl_cv_header_working_fcntl_h='no (bad O_NOFOLLOW)';; #( + 64) gl_cv_header_working_fcntl_h='no (bad O_NOATIME)';; #( + 68) gl_cv_header_working_fcntl_h='no (bad O_NOATIME, O_NOFOLLOW)';; #( + *) gl_cv_header_working_fcntl_h='no';; + esac], + [gl_cv_header_working_fcntl_h=cross-compiling])]) + + case $gl_cv_header_working_fcntl_h in #( + *O_NOATIME* | no | cross-compiling) ac_val=0;; #( + *) ac_val=1;; + esac + AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOATIME], [$ac_val], + [Define to 1 if O_NOATIME works.]) + + case $gl_cv_header_working_fcntl_h in #( + *O_NOFOLLOW* | no | cross-compiling) ac_val=0;; #( + *) ac_val=1;; + esac + AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOFOLLOW], [$ac_val], + [Define to 1 if O_NOFOLLOW works.]) +]) diff --git a/m4/gettext.m4 b/m4/gettext.m4 new file mode 100644 index 00000000..da31efee --- /dev/null +++ b/m4/gettext.m4 @@ -0,0 +1,405 @@ +# gettext.m4 serial 67 (gettext-0.19.6) +dnl Copyright (C) 1995-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995-2000. +dnl Bruno Haible , 2000-2006, 2008-2010. + +dnl Macro to add for using GNU gettext. + +dnl Usage: AM_GNU_GETTEXT([INTLSYMBOL], [NEEDSYMBOL], [INTLDIR]). +dnl INTLSYMBOL can be one of 'external', 'no-libtool', 'use-libtool'. The +dnl default (if it is not specified or empty) is 'no-libtool'. +dnl INTLSYMBOL should be 'external' for packages with no intl directory, +dnl and 'no-libtool' or 'use-libtool' for packages with an intl directory. +dnl If INTLSYMBOL is 'use-libtool', then a libtool library +dnl $(top_builddir)/intl/libintl.la will be created (shared and/or static, +dnl depending on --{enable,disable}-{shared,static} and on the presence of +dnl AM-DISABLE-SHARED). If INTLSYMBOL is 'no-libtool', a static library +dnl $(top_builddir)/intl/libintl.a will be created. +dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext +dnl implementations (in libc or libintl) without the ngettext() function +dnl will be ignored. If NEEDSYMBOL is specified and is +dnl 'need-formatstring-macros', then GNU gettext implementations that don't +dnl support the ISO C 99 formatstring macros will be ignored. +dnl INTLDIR is used to find the intl libraries. If empty, +dnl the value '$(top_builddir)/intl/' is used. +dnl +dnl The result of the configuration is one of three cases: +dnl 1) GNU gettext, as included in the intl subdirectory, will be compiled +dnl and used. +dnl Catalog format: GNU --> install in $(datadir) +dnl Catalog extension: .mo after installation, .gmo in source tree +dnl 2) GNU gettext has been found in the system's C library. +dnl Catalog format: GNU --> install in $(datadir) +dnl Catalog extension: .mo after installation, .gmo in source tree +dnl 3) No internationalization, always use English msgid. +dnl Catalog format: none +dnl Catalog extension: none +dnl If INTLSYMBOL is 'external', only cases 2 and 3 can occur. +dnl The use of .gmo is historical (it was needed to avoid overwriting the +dnl GNU format catalogs when building on a platform with an X/Open gettext), +dnl but we keep it in order not to force irrelevant filename changes on the +dnl maintainers. +dnl +AC_DEFUN([AM_GNU_GETTEXT], +[ + dnl Argument checking. + ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], , + [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT +])])])])]) + ifelse(ifelse([$1], [], [old])[]ifelse([$1], [no-libtool], [old]), [old], + [AC_DIAGNOSE([obsolete], [Use of AM_GNU_GETTEXT without [external] argument is deprecated.])]) + ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], , + [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT +])])])]) + define([gt_included_intl], + ifelse([$1], [external], + ifdef([AM_GNU_GETTEXT_][INTL_SUBDIR], [yes], [no]), + [yes])) + define([gt_libtool_suffix_prefix], ifelse([$1], [use-libtool], [l], [])) + gt_NEEDS_INIT + AM_GNU_GETTEXT_NEED([$2]) + + AC_REQUIRE([AM_PO_SUBDIRS])dnl + ifelse(gt_included_intl, yes, [ + AC_REQUIRE([AM_INTL_SUBDIR])dnl + ]) + + dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + + dnl Sometimes libintl requires libiconv, so first search for libiconv. + dnl Ideally we would do this search only after the + dnl if test "$USE_NLS" = "yes"; then + dnl if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then + dnl tests. But if configure.in invokes AM_ICONV after AM_GNU_GETTEXT + dnl the configure script would need to contain the same shell code + dnl again, outside any 'if'. There are two solutions: + dnl - Invoke AM_ICONV_LINKFLAGS_BODY here, outside any 'if'. + dnl - Control the expansions in more detail using AC_PROVIDE_IFELSE. + dnl Since AC_PROVIDE_IFELSE is only in autoconf >= 2.52 and not + dnl documented, we avoid it. + ifelse(gt_included_intl, yes, , [ + AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) + ]) + + dnl Sometimes, on Mac OS X, libintl requires linking with CoreFoundation. + gt_INTL_MACOSX + + dnl Set USE_NLS. + AC_REQUIRE([AM_NLS]) + + ifelse(gt_included_intl, yes, [ + BUILD_INCLUDED_LIBINTL=no + USE_INCLUDED_LIBINTL=no + ]) + LIBINTL= + LTLIBINTL= + POSUB= + + dnl Add a version number to the cache macros. + case " $gt_needs " in + *" need-formatstring-macros "*) gt_api_version=3 ;; + *" need-ngettext "*) gt_api_version=2 ;; + *) gt_api_version=1 ;; + esac + gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc" + gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl" + + dnl If we use NLS figure out what method + if test "$USE_NLS" = "yes"; then + gt_use_preinstalled_gnugettext=no + ifelse(gt_included_intl, yes, [ + AC_MSG_CHECKING([whether included gettext is requested]) + AC_ARG_WITH([included-gettext], + [ --with-included-gettext use the GNU gettext library included here], + nls_cv_force_use_gnu_gettext=$withval, + nls_cv_force_use_gnu_gettext=no) + AC_MSG_RESULT([$nls_cv_force_use_gnu_gettext]) + + nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext" + if test "$nls_cv_force_use_gnu_gettext" != "yes"; then + ]) + dnl User does not insist on using GNU NLS library. Figure out what + dnl to use. If GNU gettext is available we use this. Else we have + dnl to fall back to GNU NLS library. + + if test $gt_api_version -ge 3; then + gt_revision_test_code=' +#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) +#endif +changequote(,)dnl +typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; +changequote([,])dnl +' + else + gt_revision_test_code= + fi + if test $gt_api_version -ge 2; then + gt_expression_test_code=' + * ngettext ("", "", 0)' + else + gt_expression_test_code= + fi + + AC_CACHE_CHECK([for GNU gettext in libc], [$gt_func_gnugettext_libc], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +$gt_revision_test_code +extern int _nl_msg_cat_cntr; +extern int *_nl_domain_bindings; + ]], + [[ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings + ]])], + [eval "$gt_func_gnugettext_libc=yes"], + [eval "$gt_func_gnugettext_libc=no"])]) + + if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then + dnl Sometimes libintl requires libiconv, so first search for libiconv. + ifelse(gt_included_intl, yes, , [ + AM_ICONV_LINK + ]) + dnl Search for libintl and define LIBINTL, LTLIBINTL and INCINTL + dnl accordingly. Don't use AC_LIB_LINKFLAGS_BODY([intl],[iconv]) + dnl because that would add "-liconv" to LIBINTL and LTLIBINTL + dnl even if libiconv doesn't exist. + AC_LIB_LINKFLAGS_BODY([intl]) + AC_CACHE_CHECK([for GNU gettext in libintl], + [$gt_func_gnugettext_libintl], + [gt_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $INCINTL" + gt_save_LIBS="$LIBS" + LIBS="$LIBS $LIBINTL" + dnl Now see whether libintl exists and does not depend on libiconv. + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +$gt_revision_test_code +extern int _nl_msg_cat_cntr; +extern +#ifdef __cplusplus +"C" +#endif +const char *_nl_expand_alias (const char *); + ]], + [[ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("") + ]])], + [eval "$gt_func_gnugettext_libintl=yes"], + [eval "$gt_func_gnugettext_libintl=no"]) + dnl Now see whether libintl exists and depends on libiconv. + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then + LIBS="$LIBS $LIBICONV" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +$gt_revision_test_code +extern int _nl_msg_cat_cntr; +extern +#ifdef __cplusplus +"C" +#endif +const char *_nl_expand_alias (const char *); + ]], + [[ +bindtextdomain ("", ""); +return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("") + ]])], + [LIBINTL="$LIBINTL $LIBICONV" + LTLIBINTL="$LTLIBINTL $LTLIBICONV" + eval "$gt_func_gnugettext_libintl=yes" + ]) + fi + CPPFLAGS="$gt_save_CPPFLAGS" + LIBS="$gt_save_LIBS"]) + fi + + dnl If an already present or preinstalled GNU gettext() is found, + dnl use it. But if this macro is used in GNU gettext, and GNU + dnl gettext is already preinstalled in libintl, we update this + dnl libintl. (Cf. the install rule in intl/Makefile.in.) + if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \ + || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \ + && test "$PACKAGE" != gettext-runtime \ + && test "$PACKAGE" != gettext-tools; }; then + gt_use_preinstalled_gnugettext=yes + else + dnl Reset the values set by searching for libintl. + LIBINTL= + LTLIBINTL= + INCINTL= + fi + + ifelse(gt_included_intl, yes, [ + if test "$gt_use_preinstalled_gnugettext" != "yes"; then + dnl GNU gettext is not found in the C library. + dnl Fall back on included GNU gettext library. + nls_cv_use_gnu_gettext=yes + fi + fi + + if test "$nls_cv_use_gnu_gettext" = "yes"; then + dnl Mark actions used to generate GNU NLS library. + BUILD_INCLUDED_LIBINTL=yes + USE_INCLUDED_LIBINTL=yes + LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LIBICONV $LIBTHREAD" + LTLIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LTLIBICONV $LTLIBTHREAD" + LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'` + fi + + CATOBJEXT= + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + dnl Mark actions to use GNU gettext tools. + CATOBJEXT=.gmo + fi + ]) + + if test -n "$INTL_MACOSX_LIBS"; then + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + dnl Some extra flags are needed during linking. + LIBINTL="$LIBINTL $INTL_MACOSX_LIBS" + LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS" + fi + fi + + if test "$gt_use_preinstalled_gnugettext" = "yes" \ + || test "$nls_cv_use_gnu_gettext" = "yes"; then + AC_DEFINE([ENABLE_NLS], [1], + [Define to 1 if translation of program messages to the user's native language + is requested.]) + else + USE_NLS=no + fi + fi + + AC_MSG_CHECKING([whether to use NLS]) + AC_MSG_RESULT([$USE_NLS]) + if test "$USE_NLS" = "yes"; then + AC_MSG_CHECKING([where the gettext function comes from]) + if test "$gt_use_preinstalled_gnugettext" = "yes"; then + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then + gt_source="external libintl" + else + gt_source="libc" + fi + else + gt_source="included intl directory" + fi + AC_MSG_RESULT([$gt_source]) + fi + + if test "$USE_NLS" = "yes"; then + + if test "$gt_use_preinstalled_gnugettext" = "yes"; then + if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then + AC_MSG_CHECKING([how to link with libintl]) + AC_MSG_RESULT([$LIBINTL]) + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL]) + fi + + dnl For backward compatibility. Some packages may be using this. + AC_DEFINE([HAVE_GETTEXT], [1], + [Define if the GNU gettext() function is already present or preinstalled.]) + AC_DEFINE([HAVE_DCGETTEXT], [1], + [Define if the GNU dcgettext() function is already present or preinstalled.]) + fi + + dnl We need to process the po/ directory. + POSUB=po + fi + + ifelse(gt_included_intl, yes, [ + dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL + dnl to 'yes' because some of the testsuite requires it. + if test "$PACKAGE" = gettext-runtime || test "$PACKAGE" = gettext-tools; then + BUILD_INCLUDED_LIBINTL=yes + fi + + dnl Make all variables we use known to autoconf. + AC_SUBST([BUILD_INCLUDED_LIBINTL]) + AC_SUBST([USE_INCLUDED_LIBINTL]) + AC_SUBST([CATOBJEXT]) + + dnl For backward compatibility. Some configure.ins may be using this. + nls_cv_header_intl= + nls_cv_header_libgt= + + dnl For backward compatibility. Some Makefiles may be using this. + DATADIRNAME=share + AC_SUBST([DATADIRNAME]) + + dnl For backward compatibility. Some Makefiles may be using this. + INSTOBJEXT=.mo + AC_SUBST([INSTOBJEXT]) + + dnl For backward compatibility. Some Makefiles may be using this. + GENCAT=gencat + AC_SUBST([GENCAT]) + + dnl For backward compatibility. Some Makefiles may be using this. + INTLOBJS= + if test "$USE_INCLUDED_LIBINTL" = yes; then + INTLOBJS="\$(GETTOBJS)" + fi + AC_SUBST([INTLOBJS]) + + dnl Enable libtool support if the surrounding package wishes it. + INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix + AC_SUBST([INTL_LIBTOOL_SUFFIX_PREFIX]) + ]) + + dnl For backward compatibility. Some Makefiles may be using this. + INTLLIBS="$LIBINTL" + AC_SUBST([INTLLIBS]) + + dnl Make all documented variables known to autoconf. + AC_SUBST([LIBINTL]) + AC_SUBST([LTLIBINTL]) + AC_SUBST([POSUB]) +]) + + +dnl gt_NEEDS_INIT ensures that the gt_needs variable is initialized. +m4_define([gt_NEEDS_INIT], +[ + m4_divert_text([DEFAULTS], [gt_needs=]) + m4_define([gt_NEEDS_INIT], []) +]) + + +dnl Usage: AM_GNU_GETTEXT_NEED([NEEDSYMBOL]) +AC_DEFUN([AM_GNU_GETTEXT_NEED], +[ + m4_divert_text([INIT_PREPARE], [gt_needs="$gt_needs $1"]) +]) + + +dnl Usage: AM_GNU_GETTEXT_VERSION([gettext-version]) +AC_DEFUN([AM_GNU_GETTEXT_VERSION], []) + + +dnl Usage: AM_GNU_GETTEXT_REQUIRE_VERSION([gettext-version]) +AC_DEFUN([AM_GNU_GETTEXT_REQUIRE_VERSION], []) diff --git a/m4/glib-gettext.m4 b/m4/glib-gettext.m4 new file mode 100644 index 00000000..155b1d83 --- /dev/null +++ b/m4/glib-gettext.m4 @@ -0,0 +1,436 @@ +# Copyright (C) 1995-2002 Free Software Foundation, Inc. +# Copyright (C) 2001-2003,2004 Red Hat, Inc. +# +# This file is free software, distributed under the terms of the GNU +# General Public License. As a special exception to the GNU General +# Public License, this file may be distributed as part of a program +# that contains a configuration script generated by Autoconf, under +# the same distribution terms as the rest of that program. +# +# This file can be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# +# Macro to add for using GNU gettext. +# Ulrich Drepper , 1995, 1996 +# +# Modified to never use included libintl. +# Owen Taylor , 12/15/1998 +# +# Major rework to remove unused code +# Owen Taylor , 12/11/2002 +# +# Added better handling of ALL_LINGUAS from GNU gettext version +# written by Bruno Haible, Owen Taylor 5/30/3002 +# +# Modified to require ngettext +# Matthias Clasen 08/06/2004 +# +# We need this here as well, since someone might use autoconf-2.5x +# to configure GLib then an older version to configure a package +# using AM_GLIB_GNU_GETTEXT +AC_PREREQ(2.53) + +dnl +dnl We go to great lengths to make sure that aclocal won't +dnl try to pull in the installed version of these macros +dnl when running aclocal in the glib directory. +dnl +m4_copy([AC_DEFUN],[glib_DEFUN]) +m4_copy([AC_REQUIRE],[glib_REQUIRE]) +dnl +dnl At the end, if we're not within glib, we'll define the public +dnl definitions in terms of our private definitions. +dnl + +# GLIB_LC_MESSAGES +#-------------------- +glib_DEFUN([GLIB_LC_MESSAGES], + [AC_CHECK_HEADERS([locale.h]) + if test $ac_cv_header_locale_h = yes; then + AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, + [AC_TRY_LINK([#include ], [return LC_MESSAGES], + am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) + if test $am_cv_val_LC_MESSAGES = yes; then + AC_DEFINE(HAVE_LC_MESSAGES, 1, + [Define if your file defines LC_MESSAGES.]) + fi + fi]) + +# GLIB_PATH_PROG_WITH_TEST +#---------------------------- +dnl GLIB_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, +dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) +glib_DEFUN([GLIB_PATH_PROG_WITH_TEST], +[# Extract the first word of "$2", so it can be a program name with args. +set dummy $2; ac_word=[$]2 +AC_MSG_CHECKING([for $ac_word]) +AC_CACHE_VAL(ac_cv_path_$1, +[case "[$]$1" in + /*) + ac_cv_path_$1="[$]$1" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in ifelse([$5], , $PATH, [$5]); do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if [$3]; then + ac_cv_path_$1="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" +dnl If no 4th arg is given, leave the cache variable unset, +dnl so AC_PATH_PROGS will keep looking. +ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" +])dnl + ;; +esac])dnl +$1="$ac_cv_path_$1" +if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then + AC_MSG_RESULT([$]$1) +else + AC_MSG_RESULT(no) +fi +AC_SUBST($1)dnl +]) + +# GLIB_WITH_NLS +#----------------- +glib_DEFUN([GLIB_WITH_NLS], + dnl NLS is obligatory + [USE_NLS=yes + AC_SUBST(USE_NLS) + + gt_cv_have_gettext=no + + CATOBJEXT=NONE + XGETTEXT=: + INTLLIBS= + + AC_CHECK_HEADER(libintl.h, + [gt_cv_func_dgettext_libintl="no" + libintl_extra_libs="" + + # + # First check in libc + # + AC_CACHE_CHECK([for ngettext in libc], gt_cv_func_ngettext_libc, + [AC_TRY_LINK([ +#include +], + [return !ngettext ("","", 1)], + gt_cv_func_ngettext_libc=yes, + gt_cv_func_ngettext_libc=no) + ]) + + if test "$gt_cv_func_ngettext_libc" = "yes" ; then + AC_CACHE_CHECK([for dgettext in libc], gt_cv_func_dgettext_libc, + [AC_TRY_LINK([ +#include +], + [return !dgettext ("","")], + gt_cv_func_dgettext_libc=yes, + gt_cv_func_dgettext_libc=no) + ]) + fi + + if test "$gt_cv_func_ngettext_libc" = "yes" ; then + AC_CHECK_FUNCS(bind_textdomain_codeset) + fi + + # + # If we don't have everything we want, check in libintl + # + if test "$gt_cv_func_dgettext_libc" != "yes" \ + || test "$gt_cv_func_ngettext_libc" != "yes" \ + || test "$ac_cv_func_bind_textdomain_codeset" != "yes" ; then + + AC_CHECK_LIB(intl, bindtextdomain, + [AC_CHECK_LIB(intl, ngettext, + [AC_CHECK_LIB(intl, dgettext, + gt_cv_func_dgettext_libintl=yes)])]) + + if test "$gt_cv_func_dgettext_libintl" != "yes" ; then + AC_MSG_CHECKING([if -liconv is needed to use gettext]) + AC_MSG_RESULT([]) + AC_CHECK_LIB(intl, ngettext, + [AC_CHECK_LIB(intl, dcgettext, + [gt_cv_func_dgettext_libintl=yes + libintl_extra_libs=-liconv], + :,-liconv)], + :,-liconv) + fi + + # + # If we found libintl, then check in it for bind_textdomain_codeset(); + # we'll prefer libc if neither have bind_textdomain_codeset(), + # and both have dgettext and ngettext + # + if test "$gt_cv_func_dgettext_libintl" = "yes" ; then + glib_save_LIBS="$LIBS" + LIBS="$LIBS -lintl $libintl_extra_libs" + unset ac_cv_func_bind_textdomain_codeset + AC_CHECK_FUNCS(bind_textdomain_codeset) + LIBS="$glib_save_LIBS" + + if test "$ac_cv_func_bind_textdomain_codeset" = "yes" ; then + gt_cv_func_dgettext_libc=no + else + if test "$gt_cv_func_dgettext_libc" = "yes" \ + && test "$gt_cv_func_ngettext_libc" = "yes"; then + gt_cv_func_dgettext_libintl=no + fi + fi + fi + fi + + if test "$gt_cv_func_dgettext_libc" = "yes" \ + || test "$gt_cv_func_dgettext_libintl" = "yes"; then + gt_cv_have_gettext=yes + fi + + if test "$gt_cv_func_dgettext_libintl" = "yes"; then + INTLLIBS="-lintl $libintl_extra_libs" + fi + + if test "$gt_cv_have_gettext" = "yes"; then + AC_DEFINE(HAVE_GETTEXT,1, + [Define if the GNU gettext() function is already present or preinstalled.]) + GLIB_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"], no)dnl + if test "$MSGFMT" != "no"; then + glib_save_LIBS="$LIBS" + LIBS="$LIBS $INTLLIBS" + AC_CHECK_FUNCS(dcgettext) + MSGFMT_OPTS= + AC_MSG_CHECKING([if msgfmt accepts -c]) + GLIB_RUN_PROG([$MSGFMT -c -o /dev/null],[ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: test 1.0\n" +"PO-Revision-Date: 2007-02-15 12:01+0100\n" +"Last-Translator: test \n" +"Language-Team: C \n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" +], [MSGFMT_OPTS=-c; AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) + AC_SUBST(MSGFMT_OPTS) + AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) + GLIB_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :) + AC_TRY_LINK(, [extern int _nl_msg_cat_cntr; + return _nl_msg_cat_cntr], + [CATOBJEXT=.gmo + DATADIRNAME=share], + [case $host in + *-*-solaris*) + dnl On Solaris, if bind_textdomain_codeset is in libc, + dnl GNU format message catalog is always supported, + dnl since both are added to the libc all together. + dnl Hence, we'd like to go with DATADIRNAME=share and + dnl and CATOBJEXT=.gmo in this case. + AC_CHECK_FUNC(bind_textdomain_codeset, + [CATOBJEXT=.gmo + DATADIRNAME=share], + [CATOBJEXT=.mo + DATADIRNAME=lib]) + ;; + *-*-openbsd*) + CATOBJEXT=.mo + DATADIRNAME=share + ;; + *) + CATOBJEXT=.mo + DATADIRNAME=lib + ;; + esac]) + LIBS="$glib_save_LIBS" + INSTOBJEXT=.mo + else + gt_cv_have_gettext=no + fi + fi + ]) + + if test "$gt_cv_have_gettext" = "yes" ; then + AC_DEFINE(ENABLE_NLS, 1, + [always defined to indicate that i18n is enabled]) + fi + + dnl Test whether we really found GNU xgettext. + if test "$XGETTEXT" != ":"; then + dnl If it is not GNU xgettext we define it as : so that the + dnl Makefiles still can work. + if $XGETTEXT --omit-header /dev/null 2> /dev/null; then + : ; + else + AC_MSG_RESULT( + [found xgettext program is not GNU xgettext; ignore it]) + XGETTEXT=":" + fi + fi + + # We need to process the po/ directory. + POSUB=po + + AC_OUTPUT_COMMANDS( + [case "$CONFIG_FILES" in *po/Makefile.in*) + sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile + esac]) + + dnl These rules are solely for the distribution goal. While doing this + dnl we only have to keep exactly one list of the available catalogs + dnl in configure.ac. + for lang in $ALL_LINGUAS; do + GMOFILES="$GMOFILES $lang.gmo" + POFILES="$POFILES $lang.po" + done + + dnl Make all variables we use known to autoconf. + AC_SUBST(CATALOGS) + AC_SUBST(CATOBJEXT) + AC_SUBST(DATADIRNAME) + AC_SUBST(GMOFILES) + AC_SUBST(INSTOBJEXT) + AC_SUBST(INTLLIBS) + AC_SUBST(PO_IN_DATADIR_TRUE) + AC_SUBST(PO_IN_DATADIR_FALSE) + AC_SUBST(POFILES) + AC_SUBST(POSUB) + ]) + +# AM_GLIB_GNU_GETTEXT +# ------------------- +# Do checks necessary for use of gettext. If a suitable implementation +# of gettext is found in either in libintl or in the C library, +# it will set INTLLIBS to the libraries needed for use of gettext +# and AC_DEFINE() HAVE_GETTEXT and ENABLE_NLS. (The shell variable +# gt_cv_have_gettext will be set to "yes".) It will also call AC_SUBST() +# on various variables needed by the Makefile.in.in installed by +# glib-gettextize. +dnl +AU_DEFUN([GLIB_GNU_GETTEXT], + [AC_REQUIRE([AC_PROG_CC])dnl + + GLIB_LC_MESSAGES + GLIB_WITH_NLS + + if test "$gt_cv_have_gettext" = "yes"; then + if test "x$ALL_LINGUAS" = "x"; then + LINGUAS= + else + AC_MSG_CHECKING(for catalogs to be installed) + NEW_LINGUAS= + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "${LINGUAS-%UNSET%}"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + NEW_LINGUAS="$NEW_LINGUAS $presentlang" + fi + done + LINGUAS=$NEW_LINGUAS + AC_MSG_RESULT($LINGUAS) + fi + + dnl Construct list of names of catalog files to be constructed. + if test -n "$LINGUAS"; then + for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done + fi + fi + + dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly + dnl find the mkinstalldirs script in another subdir but ($top_srcdir). + dnl Try to locate is. + MKINSTALLDIRS= + if test -n "$ac_aux_dir"; then + MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" + fi + if test -z "$MKINSTALLDIRS"; then + MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" + fi + AC_SUBST(MKINSTALLDIRS) + + dnl Generate list of files to be processed by xgettext which will + dnl be included in po/Makefile. + test -d po || mkdir po + if test "x$srcdir" != "x."; then + if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then + posrcprefix="$srcdir/" + else + posrcprefix="../$srcdir/" + fi + else + posrcprefix="../" + fi + rm -f po/POTFILES + sed -e "/^#/d" -e "/^\$/d" -e "s,.*, $posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \ + < $srcdir/po/POTFILES.in > po/POTFILES + ], + [[$0: This macro is deprecated. You should use upstream gettext instead.]]) + +# AM_GLIB_DEFINE_LOCALEDIR(VARIABLE) +# ------------------------------- +# Define VARIABLE to the location where catalog files will +# be installed by po/Makefile. +glib_DEFUN([GLIB_DEFINE_LOCALEDIR], +[glib_REQUIRE([GLIB_GNU_GETTEXT])dnl +glib_save_prefix="$prefix" +glib_save_exec_prefix="$exec_prefix" +glib_save_datarootdir="$datarootdir" +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix=$prefix +datarootdir=`eval echo "${datarootdir}"` +if test "x$CATOBJEXT" = "x.mo" ; then + localedir=`eval echo "${libdir}/locale"` +else + localedir=`eval echo "${datadir}/locale"` +fi +prefix="$glib_save_prefix" +exec_prefix="$glib_save_exec_prefix" +datarootdir="$glib_save_datarootdir" +AC_DEFINE_UNQUOTED($1, "$localedir", + [Define the location where the catalogs will be installed]) +]) + +dnl +dnl Now the definitions that aclocal will find +dnl +ifdef(glib_configure_ac,[],[ +AC_DEFUN([AM_GLIB_GNU_GETTEXT],[GLIB_GNU_GETTEXT($@)]) +AC_DEFUN([AM_GLIB_DEFINE_LOCALEDIR],[GLIB_DEFINE_LOCALEDIR($@)]) +])dnl + +# GLIB_RUN_PROG(PROGRAM, TEST-FILE, [ACTION-IF-PASS], [ACTION-IF-FAIL]) +# +# Create a temporary file with TEST-FILE as its contents and pass the +# file name to PROGRAM. Perform ACTION-IF-PASS if PROGRAM exits with +# 0 and perform ACTION-IF-FAIL for any other exit status. +AC_DEFUN([GLIB_RUN_PROG], +[cat >conftest.foo <<_ACEOF +$2 +_ACEOF +if AC_RUN_LOG([$1 conftest.foo]); then + m4_ifval([$3], [$3], [:]) +m4_ifvaln([$4], [else $4])dnl +echo "$as_me: failed input was:" >&AS_MESSAGE_LOG_FD +sed 's/^/| /' conftest.foo >&AS_MESSAGE_LOG_FD +fi]) + diff --git a/m4/glibc2.m4 b/m4/glibc2.m4 new file mode 100644 index 00000000..216c8630 --- /dev/null +++ b/m4/glibc2.m4 @@ -0,0 +1,31 @@ +# glibc2.m4 serial 3 +dnl Copyright (C) 2000-2002, 2004, 2008, 2010-2015 Free Software Foundation, +dnl Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Test for the GNU C Library, version 2.0 or newer. +# From Bruno Haible. + +AC_DEFUN([gt_GLIBC2], + [ + AC_CACHE_CHECK([whether we are using the GNU C Library 2 or newer], + [ac_cv_gnu_library_2], + [AC_EGREP_CPP([Lucky GNU user], + [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ >= 2) && !defined __UCLIBC__ + Lucky GNU user + #endif +#endif + ], + [ac_cv_gnu_library_2=yes], + [ac_cv_gnu_library_2=no]) + ] + ) + AC_SUBST([GLIBC2]) + GLIBC2="$ac_cv_gnu_library_2" + ] +) diff --git a/m4/glibc21.m4 b/m4/glibc21.m4 new file mode 100644 index 00000000..3a971c5a --- /dev/null +++ b/m4/glibc21.m4 @@ -0,0 +1,34 @@ +# glibc21.m4 serial 5 +dnl Copyright (C) 2000-2002, 2004, 2008, 2010-2015 Free Software Foundation, +dnl Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Test for the GNU C Library, version 2.1 or newer, or uClibc. +# From Bruno Haible. + +AC_DEFUN([gl_GLIBC21], + [ + AC_CACHE_CHECK([whether we are using the GNU C Library >= 2.1 or uClibc], + [ac_cv_gnu_library_2_1], + [AC_EGREP_CPP([Lucky], + [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2) + Lucky GNU user + #endif +#endif +#ifdef __UCLIBC__ + Lucky user +#endif + ], + [ac_cv_gnu_library_2_1=yes], + [ac_cv_gnu_library_2_1=no]) + ] + ) + AC_SUBST([GLIBC21]) + GLIBC21="$ac_cv_gnu_library_2_1" + ] +) diff --git a/m4/gsettings.m4 b/m4/gsettings.m4 new file mode 100644 index 00000000..35ee5aa9 --- /dev/null +++ b/m4/gsettings.m4 @@ -0,0 +1,83 @@ +dnl GLIB_GSETTINGS +dnl Defines GSETTINGS_SCHEMAS_INSTALL which controls whether +dnl the schema should be compiled +dnl + +AC_DEFUN([GLIB_GSETTINGS], +[ + m4_pattern_allow([AM_V_GEN]) + AC_ARG_ENABLE(schemas-compile, + AS_HELP_STRING([--disable-schemas-compile], + [Disable regeneration of gschemas.compiled on install]), + [case ${enableval} in + yes) GSETTINGS_DISABLE_SCHEMAS_COMPILE="" ;; + no) GSETTINGS_DISABLE_SCHEMAS_COMPILE="1" ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-schemas-compile]) ;; + esac]) + AC_SUBST([GSETTINGS_DISABLE_SCHEMAS_COMPILE]) + PKG_PROG_PKG_CONFIG([0.16]) + AC_SUBST(gsettingsschemadir, [${datadir}/glib-2.0/schemas]) + if test x$cross_compiling != xyes; then + GLIB_COMPILE_SCHEMAS=`$PKG_CONFIG --variable glib_compile_schemas gio-2.0` + else + AC_PATH_PROG(GLIB_COMPILE_SCHEMAS, glib-compile-schemas) + fi + AC_SUBST(GLIB_COMPILE_SCHEMAS) + if test "x$GLIB_COMPILE_SCHEMAS" = "x"; then + ifelse([$2],,[AC_MSG_ERROR([glib-compile-schemas not found.])],[$2]) + else + ifelse([$1],,[:],[$1]) + fi + + GSETTINGS_RULES=' +.PHONY : uninstall-gsettings-schemas install-gsettings-schemas clean-gsettings-schemas + +mostlyclean-am: clean-gsettings-schemas + +gsettings__enum_file = $(addsuffix .enums.xml,$(gsettings_ENUM_NAMESPACE)) + +%.gschema.valid: %.gschema.xml $(gsettings__enum_file) + $(AM_V_GEN) $(GLIB_COMPILE_SCHEMAS) --strict --dry-run $(addprefix --schema-file=,$(gsettings__enum_file)) --schema-file=$< && mkdir -p [$](@D) && touch [$]@ + +all-am: $(gsettings_SCHEMAS:.xml=.valid) +uninstall-am: uninstall-gsettings-schemas +install-data-am: install-gsettings-schemas + +.SECONDARY: $(gsettings_SCHEMAS) + +install-gsettings-schemas: $(gsettings_SCHEMAS) $(gsettings__enum_file) + @$(NORMAL_INSTALL) + if test -n "$^"; then \ + test -z "$(gsettingsschemadir)" || $(MKDIR_P) "$(DESTDIR)$(gsettingsschemadir)"; \ + $(INSTALL_DATA) $^ "$(DESTDIR)$(gsettingsschemadir)"; \ + test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir); \ + fi + +uninstall-gsettings-schemas: + @$(NORMAL_UNINSTALL) + @list='\''$(gsettings_SCHEMAS) $(gsettings__enum_file)'\''; test -n "$(gsettingsschemadir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e '\''s|^.*/||'\''`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '\''$(DESTDIR)$(gsettingsschemadir)'\'' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(gsettingsschemadir)" && rm -f $$files + test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir) + +clean-gsettings-schemas: + rm -f $(gsettings_SCHEMAS:.xml=.valid) $(gsettings__enum_file) + +ifdef gsettings_ENUM_NAMESPACE +$(gsettings__enum_file): $(gsettings_ENUM_FILES) + $(AM_V_GEN) glib-mkenums --comments '\'''\'' --fhead "" --vhead " <@type@ id='\''$(gsettings_ENUM_NAMESPACE).@EnumName@'\''>" --vprod " " --vtail " " --ftail "" [$]^ > [$]@.tmp && mv [$]@.tmp [$]@ +endif +' + _GSETTINGS_SUBST(GSETTINGS_RULES) +]) + +dnl _GSETTINGS_SUBST(VARIABLE) +dnl Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST +AC_DEFUN([_GSETTINGS_SUBST], +[ +AC_SUBST([$1]) +m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])]) +] +) diff --git a/m4/iconv.m4 b/m4/iconv.m4 new file mode 100644 index 00000000..4e373631 --- /dev/null +++ b/m4/iconv.m4 @@ -0,0 +1,271 @@ +# iconv.m4 serial 19 (gettext-0.18.2) +dnl Copyright (C) 2000-2002, 2007-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], +[ + dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + + dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([iconv]) +]) + +AC_DEFUN([AM_ICONV_LINK], +[ + dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and + dnl those with the standalone portable GNU libiconv installed). + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + + dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV + dnl accordingly. + AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) + + dnl Add $INCICONV to CPPFLAGS before performing the following checks, + dnl because if the user has installed libiconv and not disabled its use + dnl via --without-libiconv-prefix, he wants to use it. The first + dnl AC_LINK_IFELSE will then fail, the second AC_LINK_IFELSE will succeed. + am_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) + + AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [ + am_cv_func_iconv="no, consider installing GNU libiconv" + am_cv_lib_iconv=no + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + ]], + [[iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);]])], + [am_cv_func_iconv=yes]) + if test "$am_cv_func_iconv" != yes; then + am_save_LIBS="$LIBS" + LIBS="$LIBS $LIBICONV" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + ]], + [[iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);]])], + [am_cv_lib_iconv=yes] + [am_cv_func_iconv=yes]) + LIBS="$am_save_LIBS" + fi + ]) + if test "$am_cv_func_iconv" = yes; then + AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [ + dnl This tests against bugs in AIX 5.1, AIX 6.1..7.1, HP-UX 11.11, + dnl Solaris 10. + am_save_LIBS="$LIBS" + if test $am_cv_lib_iconv = yes; then + LIBS="$LIBS $LIBICONV" + fi + am_cv_func_iconv_works=no + for ac_iconv_const in '' 'const'; do + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + +#ifndef ICONV_CONST +# define ICONV_CONST $ac_iconv_const +#endif + ]], + [[int result = 0; + /* Test against AIX 5.1 bug: Failures are not distinguishable from successful + returns. */ + { + iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); + if (cd_utf8_to_88591 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\342\202\254"; /* EURO SIGN */ + char buf[10]; + ICONV_CONST char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_utf8_to_88591, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + result |= 1; + iconv_close (cd_utf8_to_88591); + } + } + /* Test against Solaris 10 bug: Failures are not distinguishable from + successful returns. */ + { + iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); + if (cd_ascii_to_88591 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\263"; + char buf[10]; + ICONV_CONST char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_ascii_to_88591, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + result |= 2; + iconv_close (cd_ascii_to_88591); + } + } + /* Test against AIX 6.1..7.1 bug: Buffer overrun. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\304"; + static char buf[2] = { (char)0xDE, (char)0xAD }; + ICONV_CONST char *inptr = input; + size_t inbytesleft = 1; + char *outptr = buf; + size_t outbytesleft = 1; + size_t res = iconv (cd_88591_to_utf8, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res != (size_t)(-1) || outptr - buf > 1 || buf[1] != (char)0xAD) + result |= 4; + iconv_close (cd_88591_to_utf8); + } + } +#if 0 /* This bug could be worked around by the caller. */ + /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static ICONV_CONST char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; + char buf[50]; + ICONV_CONST char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_88591_to_utf8, + &inptr, &inbytesleft, + &outptr, &outbytesleft); + if ((int)res > 0) + result |= 8; + iconv_close (cd_88591_to_utf8); + } + } +#endif + /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is + provided. */ + if (/* Try standardized names. */ + iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) + /* Try IRIX, OSF/1 names. */ + && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) + /* Try AIX names. */ + && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) + /* Try HP-UX names. */ + && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) + result |= 16; + return result; +]])], + [am_cv_func_iconv_works=yes], , + [case "$host_os" in + aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; + *) am_cv_func_iconv_works="guessing yes" ;; + esac]) + test "$am_cv_func_iconv_works" = no || break + done + LIBS="$am_save_LIBS" + ]) + case "$am_cv_func_iconv_works" in + *no) am_func_iconv=no am_cv_lib_iconv=no ;; + *) am_func_iconv=yes ;; + esac + else + am_func_iconv=no am_cv_lib_iconv=no + fi + if test "$am_func_iconv" = yes; then + AC_DEFINE([HAVE_ICONV], [1], + [Define if you have the iconv() function and it works.]) + fi + if test "$am_cv_lib_iconv" = yes; then + AC_MSG_CHECKING([how to link with libiconv]) + AC_MSG_RESULT([$LIBICONV]) + else + dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV + dnl either. + CPPFLAGS="$am_save_CPPFLAGS" + LIBICONV= + LTLIBICONV= + fi + AC_SUBST([LIBICONV]) + AC_SUBST([LTLIBICONV]) +]) + +dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to +dnl avoid warnings like +dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required". +dnl This is tricky because of the way 'aclocal' is implemented: +dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN. +dnl Otherwise aclocal's initial scan pass would miss the macro definition. +dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN expansions. +dnl Otherwise aclocal would emit many "Use of uninitialized value $1" +dnl warnings. +m4_define([gl_iconv_AC_DEFUN], + m4_version_prereq([2.64], + [[AC_DEFUN_ONCE( + [$1], [$2])]], + [m4_ifdef([gl_00GNULIB], + [[AC_DEFUN_ONCE( + [$1], [$2])]], + [[AC_DEFUN( + [$1], [$2])]])])) +gl_iconv_AC_DEFUN([AM_ICONV], +[ + AM_ICONV_LINK + if test "$am_cv_func_iconv" = yes; then + AC_MSG_CHECKING([for iconv declaration]) + AC_CACHE_VAL([am_cv_proto_iconv], [ + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include +extern +#ifdef __cplusplus +"C" +#endif +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) +size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); +#else +size_t iconv(); +#endif + ]], + [[]])], + [am_cv_proto_iconv_arg1=""], + [am_cv_proto_iconv_arg1="const"]) + am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) + am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` + AC_MSG_RESULT([ + $am_cv_proto_iconv]) + AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1], + [Define as const if the declaration of iconv() needs const.]) + dnl Also substitute ICONV_CONST in the gnulib generated . + m4_ifdef([gl_ICONV_H_DEFAULTS], + [AC_REQUIRE([gl_ICONV_H_DEFAULTS]) + if test -n "$am_cv_proto_iconv_arg1"; then + ICONV_CONST="const" + fi + ]) + fi +]) diff --git a/m4/intdiv0.m4 b/m4/intdiv0.m4 new file mode 100644 index 00000000..44f98632 --- /dev/null +++ b/m4/intdiv0.m4 @@ -0,0 +1,87 @@ +# intdiv0.m4 serial 6 (gettext-0.18.2) +dnl Copyright (C) 2002, 2007-2008, 2010-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([gt_INTDIV0], +[ + AC_REQUIRE([AC_PROG_CC])dnl + AC_REQUIRE([AC_CANONICAL_HOST])dnl + + AC_CACHE_CHECK([whether integer division by zero raises SIGFPE], + gt_cv_int_divbyzero_sigfpe, + [ + gt_cv_int_divbyzero_sigfpe= +changequote(,)dnl + case "$host_os" in + macos* | darwin[6-9]* | darwin[1-9][0-9]*) + # On Mac OS X 10.2 or newer, just assume the same as when cross- + # compiling. If we were to perform the real test, 1 Crash Report + # dialog window would pop up. + case "$host_cpu" in + i[34567]86 | x86_64) + gt_cv_int_divbyzero_sigfpe="guessing yes" ;; + esac + ;; + esac +changequote([,])dnl + if test -z "$gt_cv_int_divbyzero_sigfpe"; then + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include + +static void +sigfpe_handler (int sig) +{ + /* Exit with code 0 if SIGFPE, with code 1 if any other signal. */ + exit (sig != SIGFPE); +} + +int x = 1; +int y = 0; +int z; +int nan; + +int main () +{ + signal (SIGFPE, sigfpe_handler); +/* IRIX and AIX (when "xlc -qcheck" is used) yield signal SIGTRAP. */ +#if (defined (__sgi) || defined (_AIX)) && defined (SIGTRAP) + signal (SIGTRAP, sigfpe_handler); +#endif +/* Linux/SPARC yields signal SIGILL. */ +#if defined (__sparc__) && defined (__linux__) + signal (SIGILL, sigfpe_handler); +#endif + + z = x / y; + nan = y / y; + exit (2); +} +]])], + [gt_cv_int_divbyzero_sigfpe=yes], + [gt_cv_int_divbyzero_sigfpe=no], + [ + # Guess based on the CPU. +changequote(,)dnl + case "$host_cpu" in + alpha* | i[34567]86 | x86_64 | m68k | s390*) + gt_cv_int_divbyzero_sigfpe="guessing yes";; + *) + gt_cv_int_divbyzero_sigfpe="guessing no";; + esac +changequote([,])dnl + ]) + fi + ]) + case "$gt_cv_int_divbyzero_sigfpe" in + *yes) value=1;; + *) value=0;; + esac + AC_DEFINE_UNQUOTED([INTDIV0_RAISES_SIGFPE], [$value], + [Define if integer division by zero raises signal SIGFPE.]) +]) diff --git a/m4/intl.m4 b/m4/intl.m4 new file mode 100644 index 00000000..70d9a9e5 --- /dev/null +++ b/m4/intl.m4 @@ -0,0 +1,304 @@ +# intl.m4 serial 29 (gettext-0.19) +dnl Copyright (C) 1995-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995-2000. +dnl Bruno Haible , 2000-2009. + +AC_PREREQ([2.60]) + +dnl Checks for all prerequisites of the intl subdirectory, +dnl except for INTL_LIBTOOL_SUFFIX_PREFIX (and possibly LIBTOOL), INTLOBJS, +dnl USE_INCLUDED_LIBINTL, BUILD_INCLUDED_LIBINTL. +AC_DEFUN([AM_INTL_SUBDIR], +[ + AC_REQUIRE([AC_PROG_INSTALL])dnl + AC_REQUIRE([AC_PROG_MKDIR_P])dnl + AC_REQUIRE([AC_PROG_CC])dnl + AC_REQUIRE([AC_CANONICAL_HOST])dnl + AC_REQUIRE([gt_GLIBC2])dnl + AC_REQUIRE([AC_PROG_RANLIB])dnl + AC_REQUIRE([gl_VISIBILITY])dnl + AC_REQUIRE([gt_INTL_SUBDIR_CORE])dnl + AC_REQUIRE([AC_TYPE_LONG_LONG_INT])dnl + AC_REQUIRE([gt_TYPE_WCHAR_T])dnl + AC_REQUIRE([gt_TYPE_WINT_T])dnl + AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) + AC_REQUIRE([gt_TYPE_INTMAX_T]) + AC_REQUIRE([gt_PRINTF_POSIX]) + AC_REQUIRE([gl_GLIBC21])dnl + AC_REQUIRE([gl_XSIZE])dnl + AC_REQUIRE([gl_FCNTL_O_FLAGS])dnl + AC_REQUIRE([gt_INTL_MACOSX])dnl + AC_REQUIRE([gl_EXTERN_INLINE])dnl + AC_REQUIRE([gt_GL_ATTRIBUTE])dnl + + dnl Support for automake's --enable-silent-rules. + case "$enable_silent_rules" in + yes) INTL_DEFAULT_VERBOSITY=0;; + no) INTL_DEFAULT_VERBOSITY=1;; + *) INTL_DEFAULT_VERBOSITY=1;; + esac + AC_SUBST([INTL_DEFAULT_VERBOSITY]) + + AC_CHECK_TYPE([ptrdiff_t], , + [AC_DEFINE([ptrdiff_t], [long], + [Define as the type of the result of subtracting two pointers, if the system doesn't define it.]) + ]) + AC_CHECK_HEADERS([features.h stddef.h stdlib.h string.h]) + AC_CHECK_FUNCS([asprintf fwprintf newlocale putenv setenv setlocale \ + snprintf strnlen wcslen wcsnlen mbrtowc wcrtomb]) + + dnl Use the _snprintf function only if it is declared (because on NetBSD it + dnl is defined as a weak alias of snprintf; we prefer to use the latter). + AC_CHECK_DECLS([_snprintf, _snwprintf], , , [#include ]) + + dnl Use the *_unlocked functions only if they are declared. + dnl (because some of them were defined without being declared in Solaris + dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built + dnl on Solaris 2.5.1 to run on Solaris 2.6). + AC_CHECK_DECLS([getc_unlocked], , , [#include ]) + + case $gt_cv_func_printf_posix in + *yes) HAVE_POSIX_PRINTF=1 ;; + *) HAVE_POSIX_PRINTF=0 ;; + esac + AC_SUBST([HAVE_POSIX_PRINTF]) + if test "$ac_cv_func_asprintf" = yes; then + HAVE_ASPRINTF=1 + else + HAVE_ASPRINTF=0 + fi + AC_SUBST([HAVE_ASPRINTF]) + if test "$ac_cv_func_snprintf" = yes; then + HAVE_SNPRINTF=1 + else + HAVE_SNPRINTF=0 + fi + AC_SUBST([HAVE_SNPRINTF]) + if test "$ac_cv_func_newlocale" = yes; then + HAVE_NEWLOCALE=1 + else + HAVE_NEWLOCALE=0 + fi + AC_SUBST([HAVE_NEWLOCALE]) + if test "$ac_cv_func_wprintf" = yes; then + HAVE_WPRINTF=1 + else + HAVE_WPRINTF=0 + fi + AC_SUBST([HAVE_WPRINTF]) + + AM_LANGINFO_CODESET + gt_LC_MESSAGES + + dnl Compilation on mingw and Cygwin needs special Makefile rules, because + dnl 1. when we install a shared library, we must arrange to export + dnl auxiliary pointer variables for every exported variable, + dnl 2. when we install a shared library and a static library simultaneously, + dnl the include file specifies __declspec(dllimport) and therefore we + dnl must arrange to define the auxiliary pointer variables for the + dnl exported variables _also_ in the static library. + if test "$enable_shared" = yes; then + case "$host_os" in + mingw* | cygwin*) is_woe32dll=yes ;; + *) is_woe32dll=no ;; + esac + else + is_woe32dll=no + fi + WOE32DLL=$is_woe32dll + AC_SUBST([WOE32DLL]) + + dnl On mingw and Cygwin, we can activate special Makefile rules which add + dnl version information to the shared libraries and executables. + case "$host_os" in + mingw* | cygwin*) is_woe32=yes ;; + *) is_woe32=no ;; + esac + WOE32=$is_woe32 + AC_SUBST([WOE32]) + if test $WOE32 = yes; then + dnl Check for a program that compiles Windows resource files. + AC_CHECK_TOOL([WINDRES], [windres]) + fi + + dnl Determine whether when creating a library, "-lc" should be passed to + dnl libtool or not. On many platforms, it is required for the libtool option + dnl -no-undefined to work. On HP-UX, however, the -lc - stored by libtool + dnl in the *.la files - makes it impossible to create multithreaded programs, + dnl because libtool also reorders the -lc to come before the -pthread, and + dnl this disables pthread_create() . + case "$host_os" in + hpux*) LTLIBC="" ;; + *) LTLIBC="-lc" ;; + esac + AC_SUBST([LTLIBC]) + + dnl Rename some macros and functions used for locking. + AH_BOTTOM([ +#define __libc_lock_t gl_lock_t +#define __libc_lock_define gl_lock_define +#define __libc_lock_define_initialized gl_lock_define_initialized +#define __libc_lock_init gl_lock_init +#define __libc_lock_lock gl_lock_lock +#define __libc_lock_unlock gl_lock_unlock +#define __libc_lock_recursive_t gl_recursive_lock_t +#define __libc_lock_define_recursive gl_recursive_lock_define +#define __libc_lock_define_initialized_recursive gl_recursive_lock_define_initialized +#define __libc_lock_init_recursive gl_recursive_lock_init +#define __libc_lock_lock_recursive gl_recursive_lock_lock +#define __libc_lock_unlock_recursive gl_recursive_lock_unlock +#define glthread_in_use libintl_thread_in_use +#define glthread_lock_init_func libintl_lock_init_func +#define glthread_lock_lock_func libintl_lock_lock_func +#define glthread_lock_unlock_func libintl_lock_unlock_func +#define glthread_lock_destroy_func libintl_lock_destroy_func +#define glthread_rwlock_init_multithreaded libintl_rwlock_init_multithreaded +#define glthread_rwlock_init_func libintl_rwlock_init_func +#define glthread_rwlock_rdlock_multithreaded libintl_rwlock_rdlock_multithreaded +#define glthread_rwlock_rdlock_func libintl_rwlock_rdlock_func +#define glthread_rwlock_wrlock_multithreaded libintl_rwlock_wrlock_multithreaded +#define glthread_rwlock_wrlock_func libintl_rwlock_wrlock_func +#define glthread_rwlock_unlock_multithreaded libintl_rwlock_unlock_multithreaded +#define glthread_rwlock_unlock_func libintl_rwlock_unlock_func +#define glthread_rwlock_destroy_multithreaded libintl_rwlock_destroy_multithreaded +#define glthread_rwlock_destroy_func libintl_rwlock_destroy_func +#define glthread_recursive_lock_init_multithreaded libintl_recursive_lock_init_multithreaded +#define glthread_recursive_lock_init_func libintl_recursive_lock_init_func +#define glthread_recursive_lock_lock_multithreaded libintl_recursive_lock_lock_multithreaded +#define glthread_recursive_lock_lock_func libintl_recursive_lock_lock_func +#define glthread_recursive_lock_unlock_multithreaded libintl_recursive_lock_unlock_multithreaded +#define glthread_recursive_lock_unlock_func libintl_recursive_lock_unlock_func +#define glthread_recursive_lock_destroy_multithreaded libintl_recursive_lock_destroy_multithreaded +#define glthread_recursive_lock_destroy_func libintl_recursive_lock_destroy_func +#define glthread_once_func libintl_once_func +#define glthread_once_singlethreaded libintl_once_singlethreaded +#define glthread_once_multithreaded libintl_once_multithreaded +]) +]) + + +dnl Checks for the core files of the intl subdirectory: +dnl dcigettext.c +dnl eval-plural.h +dnl explodename.c +dnl finddomain.c +dnl gettextP.h +dnl gmo.h +dnl hash-string.h hash-string.c +dnl l10nflist.c +dnl libgnuintl.h.in (except the *printf stuff) +dnl loadinfo.h +dnl loadmsgcat.c +dnl localealias.c +dnl log.c +dnl plural-exp.h plural-exp.c +dnl plural.y +dnl Used by libglocale. +AC_DEFUN([gt_INTL_SUBDIR_CORE], +[ + AC_REQUIRE([AC_C_INLINE])dnl + AC_REQUIRE([AC_TYPE_SIZE_T])dnl + AC_REQUIRE([gl_AC_HEADER_STDINT_H]) + AC_REQUIRE([AC_FUNC_ALLOCA])dnl + AC_REQUIRE([AC_FUNC_MMAP])dnl + AC_REQUIRE([gt_INTDIV0])dnl + AC_REQUIRE([gl_AC_TYPE_UINTMAX_T])dnl + AC_REQUIRE([gt_INTTYPES_PRI])dnl + AC_REQUIRE([gl_LOCK])dnl + + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[int foo (int a) { a = __builtin_expect (a, 10); return a == 10 ? 0 : 1; }]], + [[]])], + [AC_DEFINE([HAVE_BUILTIN_EXPECT], [1], + [Define to 1 if the compiler understands __builtin_expect.])]) + + AC_CHECK_HEADERS([argz.h inttypes.h limits.h unistd.h sys/param.h]) + AC_CHECK_FUNCS([getcwd getegid geteuid getgid getuid mempcpy munmap \ + stpcpy strcasecmp strdup strtoul tsearch uselocale argz_count \ + argz_stringify argz_next __fsetlocking]) + + dnl Solaris 12 provides getlocalename_l, while Illumos doesn't have + dnl it nor the equivalent. + if test $ac_cv_func_uselocale = yes; then + AC_CHECK_FUNCS([getlocalename_l]) + fi + + dnl Use the *_unlocked functions only if they are declared. + dnl (because some of them were defined without being declared in Solaris + dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built + dnl on Solaris 2.5.1 to run on Solaris 2.6). + AC_CHECK_DECLS([feof_unlocked, fgets_unlocked], , , [#include ]) + + AM_ICONV + + dnl intl/plural.c is generated from intl/plural.y. It requires bison, + dnl because plural.y uses bison specific features. It requires at least + dnl bison-2.7 for %define api.pure. + dnl bison is only needed for the maintainer (who touches plural.y). But in + dnl order to avoid separate Makefiles or --enable-maintainer-mode, we put + dnl the rule in general Makefile. Now, some people carelessly touch the + dnl files or have a broken "make" program, hence the plural.c rule will + dnl sometimes fire. To avoid an error, defines BISON to ":" if it is not + dnl present or too old. + AC_CHECK_PROGS([INTLBISON], [bison]) + if test -z "$INTLBISON"; then + ac_verc_fail=yes + else + dnl Found it, now check the version. + AC_MSG_CHECKING([version of bison]) +changequote(<<,>>)dnl + ac_prog_version=`$INTLBISON --version 2>&1 | sed -n 's/^.*GNU Bison.* \([0-9]*\.[0-9.]*\).*$/\1/p'` + case $ac_prog_version in + '') ac_prog_version="v. ?.??, bad"; ac_verc_fail=yes;; + 2.[7-9]* | [3-9].*) +changequote([,])dnl + ac_prog_version="$ac_prog_version, ok"; ac_verc_fail=no;; + *) ac_prog_version="$ac_prog_version, bad"; ac_verc_fail=yes;; + esac + AC_MSG_RESULT([$ac_prog_version]) + fi + if test $ac_verc_fail = yes; then + INTLBISON=: + fi +]) + +dnl Copies _GL_UNUSED and _GL_ATTRIBUTE_PURE definitions from +dnl gnulib-common.m4 as a fallback, if the project isn't using Gnulib. +AC_DEFUN([gt_GL_ATTRIBUTE], [ + m4_ifndef([gl_[]COMMON], + AH_VERBATIM([gt_gl_attribute], +[/* Define as a marker that can be attached to declarations that might not + be used. This helps to reduce warnings, such as from + GCC -Wunused-parameter. */ +#ifndef _GL_UNUSED +# if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define _GL_UNUSED __attribute__ ((__unused__)) +# else +# define _GL_UNUSED +# endif +#endif + +/* The __pure__ attribute was added in gcc 2.96. */ +#ifndef _GL_ATTRIBUTE_PURE +# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) +# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define _GL_ATTRIBUTE_PURE /* empty */ +# endif +#endif +]))]) diff --git a/m4/intldir.m4 b/m4/intldir.m4 new file mode 100644 index 00000000..73b672a5 --- /dev/null +++ b/m4/intldir.m4 @@ -0,0 +1,19 @@ +# intldir.m4 serial 2 (gettext-0.18) +dnl Copyright (C) 2006, 2009-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +AC_PREREQ([2.52]) + +dnl Tells the AM_GNU_GETTEXT macro to consider an intl/ directory. +AC_DEFUN([AM_GNU_GETTEXT_INTL_SUBDIR], []) diff --git a/m4/intlmacosx.m4 b/m4/intlmacosx.m4 new file mode 100644 index 00000000..bba7b3d5 --- /dev/null +++ b/m4/intlmacosx.m4 @@ -0,0 +1,56 @@ +# intlmacosx.m4 serial 5 (gettext-0.18.2) +dnl Copyright (C) 2004-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Checks for special options needed on Mac OS X. +dnl Defines INTL_MACOSX_LIBS. +AC_DEFUN([gt_INTL_MACOSX], +[ + dnl Check for API introduced in Mac OS X 10.2. + AC_CACHE_CHECK([for CFPreferencesCopyAppValue], + [gt_cv_func_CFPreferencesCopyAppValue], + [gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[CFPreferencesCopyAppValue(NULL, NULL)]])], + [gt_cv_func_CFPreferencesCopyAppValue=yes], + [gt_cv_func_CFPreferencesCopyAppValue=no]) + LIBS="$gt_save_LIBS"]) + if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then + AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], [1], + [Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in the CoreFoundation framework.]) + fi + dnl Check for API introduced in Mac OS X 10.3. + AC_CACHE_CHECK([for CFLocaleCopyCurrent], [gt_cv_func_CFLocaleCopyCurrent], + [gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[CFLocaleCopyCurrent();]])], + [gt_cv_func_CFLocaleCopyCurrent=yes], + [gt_cv_func_CFLocaleCopyCurrent=no]) + LIBS="$gt_save_LIBS"]) + if test $gt_cv_func_CFLocaleCopyCurrent = yes; then + AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], [1], + [Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the CoreFoundation framework.]) + fi + INTL_MACOSX_LIBS= + if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then + INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation" + fi + AC_SUBST([INTL_MACOSX_LIBS]) +]) diff --git a/m4/intltool.m4 b/m4/intltool.m4 new file mode 100644 index 00000000..c25b7b1a --- /dev/null +++ b/m4/intltool.m4 @@ -0,0 +1,212 @@ +## intltool.m4 - Configure intltool for the target system. -*-Shell-script-*- +## Copyright (C) 2001 Eazel, Inc. +## Author: Maciej Stachowiak +## Kenneth Christiansen +## +## 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, write to the Free Software +## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +## +## As a special exception to the GNU General Public License, if you +## distribute this file as part of a program that contains a +## configuration script generated by Autoconf, you may include it under +## the same distribution terms that you use for the rest of that program. + +dnl IT_PROG_INTLTOOL([MINIMUM-VERSION], [no-xml]) +# serial 42 IT_PROG_INTLTOOL +AC_DEFUN([IT_PROG_INTLTOOL], [ +AC_PREREQ([2.50])dnl +AC_REQUIRE([AM_NLS])dnl + +case "$am__api_version" in + 1.[01234]) + AC_MSG_ERROR([Automake 1.5 or newer is required to use intltool]) + ;; + *) + ;; +esac + +INTLTOOL_REQUIRED_VERSION_AS_INT=`echo $1 | awk -F. '{ print $ 1 * 1000 + $ 2 * 100 + $ 3; }'` +INTLTOOL_APPLIED_VERSION=`intltool-update --version | head -1 | cut -d" " -f3` +INTLTOOL_APPLIED_VERSION_AS_INT=`echo $INTLTOOL_APPLIED_VERSION | awk -F. '{ print $ 1 * 1000 + $ 2 * 100 + $ 3; }'` +if test -n "$1"; then + AC_MSG_CHECKING([for intltool >= $1]) + AC_MSG_RESULT([$INTLTOOL_APPLIED_VERSION found]) + test "$INTLTOOL_APPLIED_VERSION_AS_INT" -ge "$INTLTOOL_REQUIRED_VERSION_AS_INT" || + AC_MSG_ERROR([Your intltool is too old. You need intltool $1 or later.]) +fi + +AC_PATH_PROG(INTLTOOL_UPDATE, [intltool-update]) +AC_PATH_PROG(INTLTOOL_MERGE, [intltool-merge]) +AC_PATH_PROG(INTLTOOL_EXTRACT, [intltool-extract]) +if test -z "$INTLTOOL_UPDATE" -o -z "$INTLTOOL_MERGE" -o -z "$INTLTOOL_EXTRACT"; then + AC_MSG_ERROR([The intltool scripts were not found. Please install intltool.]) +fi + +if test -z "$AM_DEFAULT_VERBOSITY"; then + AM_DEFAULT_VERBOSITY=1 +fi +AC_SUBST([AM_DEFAULT_VERBOSITY]) + +INTLTOOL_V_MERGE='$(INTLTOOL__v_MERGE_$(V))' +INTLTOOL__v_MERGE_='$(INTLTOOL__v_MERGE_$(AM_DEFAULT_VERBOSITY))' +INTLTOOL__v_MERGE_0='@echo " ITMRG " [$]@;' +AC_SUBST(INTLTOOL_V_MERGE) +AC_SUBST(INTLTOOL__v_MERGE_) +AC_SUBST(INTLTOOL__v_MERGE_0) + +INTLTOOL_V_MERGE_OPTIONS='$(intltool__v_merge_options_$(V))' +intltool__v_merge_options_='$(intltool__v_merge_options_$(AM_DEFAULT_VERBOSITY))' +intltool__v_merge_options_0='-q' +AC_SUBST(INTLTOOL_V_MERGE_OPTIONS) +AC_SUBST(intltool__v_merge_options_) +AC_SUBST(intltool__v_merge_options_0) + + INTLTOOL_DESKTOP_RULE='%.desktop: %.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' +INTLTOOL_DIRECTORY_RULE='%.directory: %.directory.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_KEYS_RULE='%.keys: %.keys.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -k -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_PROP_RULE='%.prop: %.prop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_OAF_RULE='%.oaf: %.oaf.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -o -p $(top_srcdir)/po $< [$]@' + INTLTOOL_PONG_RULE='%.pong: %.pong.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_SERVER_RULE='%.server: %.server.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -o -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_SHEET_RULE='%.sheet: %.sheet.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' +INTLTOOL_SOUNDLIST_RULE='%.soundlist: %.soundlist.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_UI_RULE='%.ui: %.ui.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_XML_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' +if test "$INTLTOOL_APPLIED_VERSION_AS_INT" -ge 5000; then + INTLTOOL_XML_NOMERGE_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u --no-translations $< [$]@' +else + INTLTOOL_XML_NOMERGE_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) ; $(INTLTOOL_V_MERGE)_it_tmp_dir=tmp.intltool.[$][$]RANDOM && mkdir [$][$]_it_tmp_dir && LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u [$][$]_it_tmp_dir $< [$]@ && rmdir [$][$]_it_tmp_dir' +fi + INTLTOOL_XAM_RULE='%.xam: %.xml.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_KBD_RULE='%.kbd: %.kbd.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u -m -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_CAVES_RULE='%.caves: %.caves.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_SCHEMAS_RULE='%.schemas: %.schemas.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -s -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_THEME_RULE='%.theme: %.theme.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_SERVICE_RULE='%.service: %.service.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + INTLTOOL_POLICY_RULE='%.policy: %.policy.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; $(INTLTOOL_V_MERGE)LC_ALL=C $(INTLTOOL_MERGE) $(INTLTOOL_V_MERGE_OPTIONS) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' + +_IT_SUBST(INTLTOOL_DESKTOP_RULE) +_IT_SUBST(INTLTOOL_DIRECTORY_RULE) +_IT_SUBST(INTLTOOL_KEYS_RULE) +_IT_SUBST(INTLTOOL_PROP_RULE) +_IT_SUBST(INTLTOOL_OAF_RULE) +_IT_SUBST(INTLTOOL_PONG_RULE) +_IT_SUBST(INTLTOOL_SERVER_RULE) +_IT_SUBST(INTLTOOL_SHEET_RULE) +_IT_SUBST(INTLTOOL_SOUNDLIST_RULE) +_IT_SUBST(INTLTOOL_UI_RULE) +_IT_SUBST(INTLTOOL_XAM_RULE) +_IT_SUBST(INTLTOOL_KBD_RULE) +_IT_SUBST(INTLTOOL_XML_RULE) +_IT_SUBST(INTLTOOL_XML_NOMERGE_RULE) +_IT_SUBST(INTLTOOL_CAVES_RULE) +_IT_SUBST(INTLTOOL_SCHEMAS_RULE) +_IT_SUBST(INTLTOOL_THEME_RULE) +_IT_SUBST(INTLTOOL_SERVICE_RULE) +_IT_SUBST(INTLTOOL_POLICY_RULE) + +# Check the gettext tools to make sure they are GNU +AC_PATH_PROG(XGETTEXT, xgettext) +AC_PATH_PROG(MSGMERGE, msgmerge) +AC_PATH_PROG(MSGFMT, msgfmt) +AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) +if test -z "$XGETTEXT" -o -z "$MSGMERGE" -o -z "$MSGFMT"; then + AC_MSG_ERROR([GNU gettext tools not found; required for intltool]) +fi +xgversion="`$XGETTEXT --version|grep '(GNU ' 2> /dev/null`" +mmversion="`$MSGMERGE --version|grep '(GNU ' 2> /dev/null`" +mfversion="`$MSGFMT --version|grep '(GNU ' 2> /dev/null`" +if test -z "$xgversion" -o -z "$mmversion" -o -z "$mfversion"; then + AC_MSG_ERROR([GNU gettext tools not found; required for intltool]) +fi + +AC_PATH_PROG(INTLTOOL_PERL, perl) +if test -z "$INTLTOOL_PERL"; then + AC_MSG_ERROR([perl not found]) +fi +AC_MSG_CHECKING([for perl >= 5.8.1]) +$INTLTOOL_PERL -e "use 5.8.1;" > /dev/null 2>&1 +if test $? -ne 0; then + AC_MSG_ERROR([perl 5.8.1 is required for intltool]) +else + IT_PERL_VERSION=`$INTLTOOL_PERL -e "printf '%vd', $^V"` + AC_MSG_RESULT([$IT_PERL_VERSION]) +fi +if test "x$2" != "xno-xml"; then + AC_MSG_CHECKING([for XML::Parser]) + if `$INTLTOOL_PERL -e "require XML::Parser" 2>/dev/null`; then + AC_MSG_RESULT([ok]) + else + AC_MSG_ERROR([XML::Parser perl module is required for intltool]) + fi +fi + +# Substitute ALL_LINGUAS so we can use it in po/Makefile +AC_SUBST(ALL_LINGUAS) + +IT_PO_SUBDIR([po]) + +]) + + +# IT_PO_SUBDIR(DIRNAME) +# --------------------- +# All po subdirs have to be declared with this macro; the subdir "po" is +# declared by IT_PROG_INTLTOOL. +# +AC_DEFUN([IT_PO_SUBDIR], +[AC_PREREQ([2.53])dnl We use ac_top_srcdir inside AC_CONFIG_COMMANDS. +dnl +dnl The following CONFIG_COMMANDS should be executed at the very end +dnl of config.status. +AC_CONFIG_COMMANDS_PRE([ + AC_CONFIG_COMMANDS([$1/stamp-it], [ + if [ ! grep "^# INTLTOOL_MAKEFILE$" "$1/Makefile.in" > /dev/null ]; then + AC_MSG_ERROR([$1/Makefile.in.in was not created by intltoolize.]) + fi + rm -f "$1/stamp-it" "$1/stamp-it.tmp" "$1/POTFILES" "$1/Makefile.tmp" + >"$1/stamp-it.tmp" + [sed '/^#/d + s/^[[].*] *// + /^[ ]*$/d + '"s|^| $ac_top_srcdir/|" \ + "$srcdir/$1/POTFILES.in" | sed '$!s/$/ \\/' >"$1/POTFILES" + ] + [sed '/^POTFILES =/,/[^\\]$/ { + /^POTFILES =/!d + r $1/POTFILES + } + ' "$1/Makefile.in" >"$1/Makefile"] + rm -f "$1/Makefile.tmp" + mv "$1/stamp-it.tmp" "$1/stamp-it" + ]) +])dnl +]) + +# _IT_SUBST(VARIABLE) +# ------------------- +# Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST +# +AC_DEFUN([_IT_SUBST], +[ +AC_SUBST([$1]) +m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])]) +] +) + +# deprecated macros +AU_ALIAS([AC_PROG_INTLTOOL], [IT_PROG_INTLTOOL]) +# A hint is needed for aclocal from Automake <= 1.9.4: +# AC_DEFUN([AC_PROG_INTLTOOL], ...) + diff --git a/m4/intmax.m4 b/m4/intmax.m4 new file mode 100644 index 00000000..08b3a382 --- /dev/null +++ b/m4/intmax.m4 @@ -0,0 +1,36 @@ +# intmax.m4 serial 6 (gettext-0.18.2) +dnl Copyright (C) 2002-2005, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Test whether the system has the 'intmax_t' type, but don't attempt to +dnl find a replacement if it is lacking. + +AC_DEFUN([gt_TYPE_INTMAX_T], +[ + AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) + AC_REQUIRE([gl_AC_HEADER_STDINT_H]) + AC_CACHE_CHECK([for intmax_t], [gt_cv_c_intmax_t], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include +#if HAVE_STDINT_H_WITH_UINTMAX +#include +#endif +#if HAVE_INTTYPES_H_WITH_UINTMAX +#include +#endif + ]], + [[intmax_t x = -1; + return !x;]])], + [gt_cv_c_intmax_t=yes], + [gt_cv_c_intmax_t=no])]) + if test $gt_cv_c_intmax_t = yes; then + AC_DEFINE([HAVE_INTMAX_T], [1], + [Define if you have the 'intmax_t' type in or .]) + fi +]) diff --git a/m4/introspection.m4 b/m4/introspection.m4 new file mode 100644 index 00000000..d89c3d90 --- /dev/null +++ b/m4/introspection.m4 @@ -0,0 +1,96 @@ +dnl -*- mode: autoconf -*- +dnl Copyright 2009 Johan Dahlin +dnl +dnl This file is free software; the author(s) gives unlimited +dnl permission to copy and/or distribute it, with or without +dnl modifications, as long as this notice is preserved. +dnl + +# serial 1 + +m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], +[ + AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([LT_INIT],[$0])dnl setup libtool first + + dnl enable/disable introspection + m4_if([$2], [require], + [dnl + enable_introspection=yes + ],[dnl + AC_ARG_ENABLE(introspection, + AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], + [Enable introspection for this build]),, + [enable_introspection=auto]) + ])dnl + + AC_MSG_CHECKING([for gobject-introspection]) + + dnl presence/version checking + AS_CASE([$enable_introspection], + [no], [dnl + found_introspection="no (disabled, use --enable-introspection to enable)" + ],dnl + [yes],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0],, + AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], + found_introspection=yes, + AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) + ],dnl + [auto],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) + dnl Canonicalize enable_introspection + enable_introspection=$found_introspection + ],dnl + [dnl + AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) + ])dnl + + AC_MSG_RESULT([$found_introspection]) + + INTROSPECTION_SCANNER= + INTROSPECTION_COMPILER= + INTROSPECTION_GENERATE= + INTROSPECTION_GIRDIR= + INTROSPECTION_TYPELIBDIR= + if test "x$found_introspection" = "xyes"; then + INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` + INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` + INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` + INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` + INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" + INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` + INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` + INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection + fi + AC_SUBST(INTROSPECTION_SCANNER) + AC_SUBST(INTROSPECTION_COMPILER) + AC_SUBST(INTROSPECTION_GENERATE) + AC_SUBST(INTROSPECTION_GIRDIR) + AC_SUBST(INTROSPECTION_TYPELIBDIR) + AC_SUBST(INTROSPECTION_CFLAGS) + AC_SUBST(INTROSPECTION_LIBS) + AC_SUBST(INTROSPECTION_MAKEFILE) + + AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") +]) + + +dnl Usage: +dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) + +AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) +]) + +dnl Usage: +dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) + + +AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) +]) diff --git a/m4/inttypes-pri.m4 b/m4/inttypes-pri.m4 new file mode 100644 index 00000000..0884e71e --- /dev/null +++ b/m4/inttypes-pri.m4 @@ -0,0 +1,42 @@ +# inttypes-pri.m4 serial 7 (gettext-0.18.2) +dnl Copyright (C) 1997-2002, 2006, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_PREREQ([2.53]) + +# Define PRI_MACROS_BROKEN if exists and defines the PRI* +# macros to non-string values. This is the case on AIX 4.3.3. + +AC_DEFUN([gt_INTTYPES_PRI], +[ + AC_CHECK_HEADERS([inttypes.h]) + if test $ac_cv_header_inttypes_h = yes; then + AC_CACHE_CHECK([whether the inttypes.h PRIxNN macros are broken], + [gt_cv_inttypes_pri_broken], + [ + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#ifdef PRId32 +char *p = PRId32; +#endif + ]], + [[]])], + [gt_cv_inttypes_pri_broken=no], + [gt_cv_inttypes_pri_broken=yes]) + ]) + fi + if test "$gt_cv_inttypes_pri_broken" = yes; then + AC_DEFINE_UNQUOTED([PRI_MACROS_BROKEN], [1], + [Define if exists and defines unusable PRI* macros.]) + PRI_MACROS_BROKEN=1 + else + PRI_MACROS_BROKEN=0 + fi + AC_SUBST([PRI_MACROS_BROKEN]) +]) diff --git a/m4/inttypes_h.m4 b/m4/inttypes_h.m4 new file mode 100644 index 00000000..d0b5f5d9 --- /dev/null +++ b/m4/inttypes_h.m4 @@ -0,0 +1,29 @@ +# inttypes_h.m4 serial 10 +dnl Copyright (C) 1997-2004, 2006, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +# Define HAVE_INTTYPES_H_WITH_UINTMAX if exists, +# doesn't clash with , and declares uintmax_t. + +AC_DEFUN([gl_AC_HEADER_INTTYPES_H], +[ + AC_CACHE_CHECK([for inttypes.h], [gl_cv_header_inttypes_h], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + ]], + [[uintmax_t i = (uintmax_t) -1; return !i;]])], + [gl_cv_header_inttypes_h=yes], + [gl_cv_header_inttypes_h=no])]) + if test $gl_cv_header_inttypes_h = yes; then + AC_DEFINE_UNQUOTED([HAVE_INTTYPES_H_WITH_UINTMAX], [1], + [Define if exists, doesn't clash with , + and declares uintmax_t. ]) + fi +]) diff --git a/m4/lcmessage.m4 b/m4/lcmessage.m4 new file mode 100644 index 00000000..7470ec59 --- /dev/null +++ b/m4/lcmessage.m4 @@ -0,0 +1,35 @@ +# lcmessage.m4 serial 7 (gettext-0.18.2) +dnl Copyright (C) 1995-2002, 2004-2005, 2008-2014 Free Software Foundation, +dnl Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995. + +# Check whether LC_MESSAGES is available in . + +AC_DEFUN([gt_LC_MESSAGES], +[ + AC_CACHE_CHECK([for LC_MESSAGES], [gt_cv_val_LC_MESSAGES], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[return LC_MESSAGES]])], + [gt_cv_val_LC_MESSAGES=yes], + [gt_cv_val_LC_MESSAGES=no])]) + if test $gt_cv_val_LC_MESSAGES = yes; then + AC_DEFINE([HAVE_LC_MESSAGES], [1], + [Define if your file defines LC_MESSAGES.]) + fi +]) diff --git a/m4/lib-ld.m4 b/m4/lib-ld.m4 new file mode 100644 index 00000000..91ca9117 --- /dev/null +++ b/m4/lib-ld.m4 @@ -0,0 +1,119 @@ +# lib-ld.m4 serial 6 +dnl Copyright (C) 1996-2003, 2009-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Subroutines of libtool.m4, +dnl with replacements s/_*LT_PATH/AC_LIB_PROG/ and s/lt_/acl_/ to avoid +dnl collision with libtool.m4. + +dnl From libtool-2.4. Sets the variable with_gnu_ld to yes or no. +AC_DEFUN([AC_LIB_PROG_LD_GNU], +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], [acl_cv_prog_gnu_ld], +[# I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 /dev/null 2>&1 \ + && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ + || PATH_SEPARATOR=';' + } +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`echo "$ac_prog"| sed 's%\\\\%/%g'` + while echo "$ac_prog" | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL([acl_cv_path_LD], +[if test -z "$LD"; then + acl_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$acl_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$acl_cv_path_LD" -v 2>&1 = 1.10 to complain if config.rpath is missing. + m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) + AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS + AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host + AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir + AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [ + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + ]) + wl="$acl_cv_wl" + acl_libext="$acl_cv_libext" + acl_shlibext="$acl_cv_shlibext" + acl_libname_spec="$acl_cv_libname_spec" + acl_library_names_spec="$acl_cv_library_names_spec" + acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + acl_hardcode_direct="$acl_cv_hardcode_direct" + acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" + dnl Determine whether the user wants rpath handling at all. + AC_ARG_ENABLE([rpath], + [ --disable-rpath do not hardcode runtime library paths], + :, enable_rpath=yes) +]) + +dnl AC_LIB_FROMPACKAGE(name, package) +dnl declares that libname comes from the given package. The configure file +dnl will then not have a --with-libname-prefix option but a +dnl --with-package-prefix option. Several libraries can come from the same +dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar +dnl macro call that searches for libname. +AC_DEFUN([AC_LIB_FROMPACKAGE], +[ + pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) + define([acl_frompackage_]NAME, [$2]) + popdef([NAME]) + pushdef([PACK],[$2]) + pushdef([PACKUP],[m4_translit(PACK,[abcdefghijklmnopqrstuvwxyz./+-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) + define([acl_libsinpackage_]PACKUP, + m4_ifdef([acl_libsinpackage_]PACKUP, [m4_defn([acl_libsinpackage_]PACKUP)[, ]],)[lib$1]) + popdef([PACKUP]) + popdef([PACK]) +]) + +dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. +dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found +dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_LINKFLAGS_BODY], +[ + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + pushdef([NAME],[m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./+-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) + pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, lib[$1])]) + pushdef([PACKUP],[m4_translit(PACK,[abcdefghijklmnopqrstuvwxyz./+-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ____])]) + pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, [acl_libsinpackage_]PACKUP, lib[$1])]) + dnl Autoconf >= 2.61 supports dots in --with options. + pushdef([P_A_C_K],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[m4_translit(PACK,[.],[_])],PACK)]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_ARG_WITH(P_A_C_K[-prefix], +[[ --with-]]P_A_C_K[[-prefix[=DIR] search for ]PACKLIBS[ in DIR/include and DIR/lib + --without-]]P_A_C_K[[-prefix don't search for ]PACKLIBS[ in includedir and libdir]], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + if test "$acl_libdirstem2" != "$acl_libdirstem" \ + && ! test -d "$withval/$acl_libdirstem"; then + additional_libdir="$withval/$acl_libdirstem2" + fi + fi + fi +]) + dnl Search the library and its dependencies in $additional_libdir and + dnl $LDFLAGS. Using breadth-first-seach. + LIB[]NAME= + LTLIB[]NAME= + INC[]NAME= + LIB[]NAME[]_PREFIX= + dnl HAVE_LIB${NAME} is an indicator that LIB${NAME}, LTLIB${NAME} have been + dnl computed. So it has to be reset here. + HAVE_LIB[]NAME= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='$1 $2' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + dnl See if it was already located by an earlier AC_LIB_LINKFLAGS + dnl or AC_LIB_HAVE_LINKFLAGS call. + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./+-|ABCDEFGHIJKLMNOPQRSTUVWXYZ____|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" + else + dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined + dnl that this library doesn't exist. So just drop it. + : + fi + else + dnl Search the library lib$name in $additional_libdir and $LDFLAGS + dnl and the already constructed $LIBNAME/$LTLIBNAME. + found_dir= + found_la= + found_so= + found_a= + eval libname=\"$acl_libname_spec\" # typically: libname=lib$name + if test -n "$acl_shlibext"; then + shrext=".$acl_shlibext" # typically: shrext=.so + else + shrext= + fi + if test $use_additional = yes; then + dir="$additional_libdir" + dnl The same code as in the loop below: + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + dnl Found the library. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + dnl Linking with a shared library. We attempt to hardcode its + dnl directory into the executable's runpath, unless it's the + dnl standard /usr/lib. + if test "$enable_rpath" = no \ + || test "X$found_dir" = "X/usr/$acl_libdirstem" \ + || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then + dnl No hardcoding is needed. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + dnl The hardcoding into $LIBNAME is system dependent. + if test "$acl_hardcode_direct" = yes; then + dnl Using DIR/libNAME.so during linking hardcodes DIR into the + dnl resulting binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + dnl Rely on "-L$found_dir". + dnl But don't add it if it's already contained in the LDFLAGS + dnl or the already constructed $LIBNAME + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" + fi + if test "$acl_hardcode_minus_L" != no; then + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH + dnl here, because this doesn't fit in flags passed to the + dnl compiler. So give up. No hardcoding. This affects only + dnl very old systems. + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + dnl Linking with a static library. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" + else + dnl We shouldn't come here, but anyway it's good to have a + dnl fallback. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" + fi + fi + dnl Assume the include files are nearby. + additional_includedir= + case "$found_dir" in + */$acl_libdirstem | */$acl_libdirstem/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` + if test "$name" = '$1'; then + LIB[]NAME[]_PREFIX="$basedir" + fi + additional_includedir="$basedir/include" + ;; + */$acl_libdirstem2 | */$acl_libdirstem2/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` + if test "$name" = '$1'; then + LIB[]NAME[]_PREFIX="$basedir" + fi + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + dnl Potentially add $additional_includedir to $INCNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's /usr/local/include and we are using GCC on Linux, + dnl 3. if it's already present in $CPPFLAGS or the already + dnl constructed $INCNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INC[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $INCNAME. + INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + dnl Look for dependencies. + if test -n "$found_la"; then + dnl Read the .la file. It defines the variables + dnl dlname, library_names, old_library, dependency_libs, current, + dnl age, revision, installed, dlopen, dlpreopen, libdir. + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + dnl We use only dependency_libs. + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's /usr/local/lib and we are using GCC on Linux, + dnl 3. if it's already present in $LDFLAGS or the already + dnl constructed $LIBNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ + && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ + || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LIBNAME. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LTLIBNAME. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + dnl Handle this in the next round. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + dnl Handle this in the next round. Throw away the .la's + dnl directory; it is already contained in a preceding -L + dnl option. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + dnl Most likely an immediate library name. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" + ;; + esac + done + fi + else + dnl Didn't find the library; assume it is in the system directories + dnl known to the linker and runtime loader. (All the system + dnl directories known to the linker should also be known to the + dnl runtime loader, otherwise the system is severely misconfigured.) + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user must + dnl pass all path elements in one option. We can arrange that for a + dnl single library, but not when more than one $LIBNAMEs are used. + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" + done + dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + else + dnl The -rpath options are cumulative. + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + dnl When using libtool, the option that works for both libraries and + dnl executables is -R. The -R options are cumulative. + for found_dir in $ltrpathdirs; do + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" + done + fi + popdef([P_A_C_K]) + popdef([PACKLIBS]) + popdef([PACKUP]) + popdef([PACK]) + popdef([NAME]) +]) + +dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, +dnl unless already present in VAR. +dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes +dnl contains two or three consecutive elements that belong together. +AC_DEFUN([AC_LIB_APPENDTOVAR], +[ + for element in [$2]; do + haveit= + for x in $[$1]; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + [$1]="${[$1]}${[$1]:+ }$element" + fi + done +]) + +dnl For those cases where a variable contains several -L and -l options +dnl referring to unknown libraries and directories, this macro determines the +dnl necessary additional linker options for the runtime path. +dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) +dnl sets LDADDVAR to linker options needed together with LIBSVALUE. +dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, +dnl otherwise linking without libtool is assumed. +AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], +[ + AC_REQUIRE([AC_LIB_RPATH]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + $1= + if test "$enable_rpath" != no; then + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode directories into the resulting + dnl binary. + rpathdirs= + next= + for opt in $2; do + if test -n "$next"; then + dir="$next" + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem" \ + && test "X$dir" != "X/usr/$acl_libdirstem2"; then + rpathdirs="$rpathdirs $dir" + fi + next= + else + case $opt in + -L) next=yes ;; + -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem" \ + && test "X$dir" != "X/usr/$acl_libdirstem2"; then + rpathdirs="$rpathdirs $dir" + fi + next= ;; + *) next= ;; + esac + fi + done + if test "X$rpathdirs" != "X"; then + if test -n ""$3""; then + dnl libtool is used for linking. Use -R options. + for dir in $rpathdirs; do + $1="${$1}${$1:+ }-R$dir" + done + else + dnl The linker is used for linking directly. + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user + dnl must pass all path elements in one option. + alldirs= + for dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="$flag" + else + dnl The -rpath options are cumulative. + for dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="${$1}${$1:+ }$flag" + done + fi + fi + fi + fi + fi + AC_SUBST([$1]) +]) diff --git a/m4/lib-prefix.m4 b/m4/lib-prefix.m4 new file mode 100644 index 00000000..0465f477 --- /dev/null +++ b/m4/lib-prefix.m4 @@ -0,0 +1,224 @@ +# lib-prefix.m4 serial 7 (gettext-0.18) +dnl Copyright (C) 2001-2005, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and +dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't +dnl require excessive bracketing. +ifdef([AC_HELP_STRING], +[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], +[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) + +dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed +dnl to access previously installed libraries. The basic assumption is that +dnl a user will want packages to use other packages he previously installed +dnl with the same --prefix option. +dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate +dnl libraries, but is otherwise very convenient. +AC_DEFUN([AC_LIB_PREFIX], +[ + AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib-prefix], +[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib + --without-lib-prefix don't search for libraries in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + fi + fi +]) + if test $use_additional = yes; then + dnl Potentially add $additional_includedir to $CPPFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's already present in $CPPFLAGS, + dnl 3. if it's /usr/local/include and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + for x in $CPPFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $CPPFLAGS. + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" + fi + fi + fi + fi + dnl Potentially add $additional_libdir to $LDFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's already present in $LDFLAGS, + dnl 3. if it's /usr/local/lib and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then + haveit= + for x in $LDFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LDFLAGS. + LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" + fi + fi + fi + fi + fi +]) + +dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, +dnl acl_final_exec_prefix, containing the values to which $prefix and +dnl $exec_prefix will expand at the end of the configure script. +AC_DEFUN([AC_LIB_PREPARE_PREFIX], +[ + dnl Unfortunately, prefix and exec_prefix get only finally determined + dnl at the end of configure. + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the +dnl variables prefix and exec_prefix bound to the values they will have +dnl at the end of the configure script. +AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], +[ + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + $1 + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_PREPARE_MULTILIB creates +dnl - a variable acl_libdirstem, containing the basename of the libdir, either +dnl "lib" or "lib64" or "lib/64", +dnl - a variable acl_libdirstem2, as a secondary possible value for +dnl acl_libdirstem, either the same as acl_libdirstem or "lib/sparcv9" or +dnl "lib/amd64". +AC_DEFUN([AC_LIB_PREPARE_MULTILIB], +[ + dnl There is no formal standard regarding lib and lib64. + dnl On glibc systems, the current practice is that on a system supporting + dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under + dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. We determine + dnl the compiler's default mode by looking at the compiler's library search + dnl path. If at least one of its elements ends in /lib64 or points to a + dnl directory whose absolute pathname ends in /lib64, we assume a 64-bit ABI. + dnl Otherwise we use the default, namely "lib". + dnl On Solaris systems, the current practice is that on a system supporting + dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under + dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or + dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib. + AC_REQUIRE([AC_CANONICAL_HOST]) + acl_libdirstem=lib + acl_libdirstem2= + case "$host_os" in + solaris*) + dnl See Solaris 10 Software Developer Collection > Solaris 64-bit Developer's Guide > The Development Environment + dnl . + dnl "Portable Makefiles should refer to any library directories using the 64 symbolic link." + dnl But we want to recognize the sparcv9 or amd64 subdirectory also if the + dnl symlink is missing, so we set acl_libdirstem2 too. + AC_CACHE_CHECK([for 64-bit host], [gl_cv_solaris_64bit], + [AC_EGREP_CPP([sixtyfour bits], [ +#ifdef _LP64 +sixtyfour bits +#endif + ], [gl_cv_solaris_64bit=yes], [gl_cv_solaris_64bit=no]) + ]) + if test $gl_cv_solaris_64bit = yes; then + acl_libdirstem=lib/64 + case "$host_cpu" in + sparc*) acl_libdirstem2=lib/sparcv9 ;; + i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; + esac + fi + ;; + *) + searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` + if test -n "$searchpath"; then + acl_save_IFS="${IFS= }"; IFS=":" + for searchdir in $searchpath; do + if test -d "$searchdir"; then + case "$searchdir" in + */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; + */../ | */.. ) + # Better ignore directories of this form. They are misleading. + ;; + *) searchdir=`cd "$searchdir" && pwd` + case "$searchdir" in + */lib64 ) acl_libdirstem=lib64 ;; + esac ;; + esac + fi + done + IFS="$acl_save_IFS" + fi + ;; + esac + test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" +]) diff --git a/m4/libtool.m4 b/m4/libtool.m4 new file mode 100644 index 00000000..a644432f --- /dev/null +++ b/m4/libtool.m4 @@ -0,0 +1,8372 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool 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 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 . +]) + +# serial 58 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS=$ltmain + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_PREPARE_CC_BASENAME +# ----------------------- +m4_defun([_LT_PREPARE_CC_BASENAME], [ +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in @S|@*""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} +])# _LT_PREPARE_CC_BASENAME + + +# _LT_CC_BASENAME(CC) +# ------------------- +# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, +# but that macro is also expanded into generated libtool script, which +# arranges for $SED and $ECHO to be set by different means. +m4_defun([_LT_CC_BASENAME], +[m4_require([_LT_PREPARE_CC_BASENAME])dnl +AC_REQUIRE([_LT_DECL_SED])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl +func_cc_basename $1 +cc_basename=$func_cc_basename_result +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl +m4_require([_LT_CMD_TRUNCATE])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options that allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld=$lt_cv_prog_gnu_ld + +old_CC=$CC +old_CFLAGS=$CFLAGS + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from 'configure', and 'config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# 'config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain=$ac_aux_dir/ltmain.sh +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the 'libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to 'config.status' so that its +# declaration there will have the same value as in 'configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags='_LT_TAGS'dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into 'config.status', and then the shell code to quote escape them in +# for loops in 'config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# '#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test 0 = "$lt_write_fail" && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +'$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test 0 != $[#] +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try '$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try '$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test yes = "$silent" && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options that allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST + fi + + cfgfile=${ofile}T + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# Generated automatically by $as_me ($PACKAGE) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + +_LT_COPYING +_LT_LIBTOOL_TAGS + +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +_LT_PREPARE_MUNGE_PATH_LIST +_LT_PREPARE_CC_BASENAME + +# ### END FUNCTIONS SHARED WITH CONFIGURE + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "$LT_MULTI_MODULE"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS=$save_LDFLAGS + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[[012]][[,.]]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test yes = "$lt_cv_apple_cc_single_mod"; then + _lt_dar_single_mod='$single_module' + fi + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' + fi + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test yes = "$lt_cv_ld_force_load"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + m4_if([$1], [CXX], +[ if test yes != "$lt_cv_apple_cc_single_mod"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script that will find a shell with a builtin +# printf (that we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case $ECHO in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], + [Search for dependent libraries within DIR (or the compiler's sysroot + if not specified).])], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case $with_sysroot in #( + yes) + if test yes = "$GCC"; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([$with_sysroot]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and where our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test no = "$enable_libtool_lock" || enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE=32 + ;; + *ELF-64*) + HPUX_IA64_MODE=64 + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test yes = "$lt_cv_prog_gnu_ld"; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test yes != "$lt_cv_cc_needs_belf"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS=$SAVE_CFLAGS + fi + ;; +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks=$enable_libtool_lock +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cru} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test 0 -eq "$ac_status"; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test 0 -ne "$ac_status"; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test no = "$lt_cv_ar_at_file"; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test yes = "[$]$2"; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS +]) + +if test yes = "[$]$2"; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring=ABCD + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test 17 != "$i" # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n "$lt_cv_sys_max_cmd_len"; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes = "$cross_compiling"; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes != "$enable_dlopen"; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen=load_add_on + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen=LoadLibrary + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ + lt_cv_dlopen=dyld + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen=shl_load], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen=dlopen], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test no = "$lt_cv_dlopen"; then + enable_dlopen=no + else + enable_dlopen=yes + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS=$LDFLAGS + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS=$LIBS + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test yes = "$lt_cv_dlopen_self"; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links=nottested +if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test no = "$hard_links"; then + AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", + [Define to the sub-directory where libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then + + # We can hardcode non-existent directories. + if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && + test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || + test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_PREPARE_MUNGE_PATH_LIST +# --------------------------- +# Make sure func_munge_path_list() is defined correctly. +m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], +[[# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x@S|@2 in + x) + ;; + *:) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" + ;; + x:*) + eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" + ;; + *) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + esac +} +]])# _LT_PREPARE_PATH_LIST + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test yes = "$GCC"; then + case $host_os in + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary... + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo = "/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +AC_ARG_VAR([LT_SYS_LIBRARY_PATH], +[User-defined run-time library search path.]) + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a[(]lib.so.V[)]' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], + [Detected run-time system search path for libraries]) +_LT_DECL([], [configure_time_lt_sys_library_path], [2], + [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program that can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$1"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac]) +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program that can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test no = "$withval" || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], +[if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi]) +rm -f conftest.i conftest2.i conftest.out]) +])# _LT_PATH_DD + + +# _LT_CMD_TRUNCATE +# ---------------- +# find command to truncate a binary pipe +m4_defun([_LT_CMD_TRUNCATE], +[m4_require([_LT_PATH_DD]) +AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], +[printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) +_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], + [Command to truncate a binary pipe]) +])# _LT_CMD_TRUNCATE + + +# _LT_CHECK_MAGIC_METHOD +# ---------------------- +# how to check for library dependencies +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_MAGIC_METHOD], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +AC_CACHE_CHECK([how to recognize dependent libraries], +lt_cv_deplibs_check_method, +[lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# 'unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[[4-9]]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[[45]]*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM=$NM +else + lt_nm_to_check=${ac_tool_prefix}nm + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break 2 + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break 2 + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS=$lt_save_ifs + done + : ${lt_cv_path_NM=no} +fi]) +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols -headers" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test : != "$DUMPBIN"; then + NM=$DUMPBIN + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd=$ECHO + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test yes != "$lt_cv_path_mainfest_tool"; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# _LT_DLL_DEF_P([FILE]) +# --------------------- +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with func_dll_def_p in the libtool script +AC_DEFUN([_LT_DLL_DEF_P], +[dnl + test DEF = "`$SED -n dnl + -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace + -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments + -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl + -e q dnl Only consider the first "real" line + $1`" dnl +])# _LT_DLL_DEF_P + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM=-lm) + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test yes = "$GCC"; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test ia64 = "$host_cpu"; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS=conftstm.$ac_objext + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test yes = "$pipe_works"; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], + [Transform the output of nm into a list of symbols to manually relocate]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([nm_interface], [lt_cv_nm_interface], [1], + [The name lister interface]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64, which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd*) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test yes = "$GCC"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64, which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test yes != "$GCC"; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd* | bitrig*) + with_gnu_ld=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test yes = "$with_gnu_ld"; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test yes = "$lt_use_gnu_ld_interface"; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='$wl' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test linux-dietlibc = "$host_os"; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test no = "$tmp_diet" + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + tcc*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' + ;; + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then + aix_use_runtimelinking=yes + break + fi + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GCC"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + + hpux10*) + if test yes,no = "$GCC,$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test yes,no = "$GCC,$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS=$save_LDFLAGS]) + if test yes = "$lt_cv_irix_exported_symbol"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' + fi + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(ld_shlibs, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + osf3*) + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test yes = "$GCC"; then + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. GCC discards it without '$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test yes = "$GCC"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test sequent = "$host_vendor"; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test sni = "$host_vendor"; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting $shlibpath_var if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC=$CC +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report what library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC=$lt_save_CC +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_caught_CXX_error"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test yes = "$GXX"; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test yes = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='$wl' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GXX"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + # The "-G" linker flag allows undefined symbols. + _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require '-G' NOT '-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + + _LT_TAGVAR(GCC, $1)=$GXX + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test yes != "$_lt_caught_CXX_error" + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case @S|@2 in + .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; + *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $prev$p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test x-L = "$p" || + test x-R = "$p"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test no = "$pre_test_object_deps_done"; then + case $prev in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)=$prev$p + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test no = "$pre_test_object_deps_done"; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)=$p + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)=$p + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test no = "$F77"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_F77"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$G77 + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_F77" + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test no = "$FC"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_FC"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_FC" + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code=$lt_simple_compile_test_code + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f "$lt_ac_sed" && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test 10 -lt "$lt_ac_count" && break + lt_ac_count=`expr $lt_ac_count + 1` + if test "$lt_ac_count" -gt "$lt_ac_max"; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine what file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS diff --git a/m4/lock.m4 b/m4/lock.m4 new file mode 100644 index 00000000..9fd4aa78 --- /dev/null +++ b/m4/lock.m4 @@ -0,0 +1,42 @@ +# lock.m4 serial 13 (gettext-0.18.2) +dnl Copyright (C) 2005-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([gl_LOCK], +[ + AC_REQUIRE([gl_THREADLIB]) + if test "$gl_threads_api" = posix; then + # OSF/1 4.0 and Mac OS X 10.1 lack the pthread_rwlock_t type and the + # pthread_rwlock_* functions. + AC_CHECK_TYPE([pthread_rwlock_t], + [AC_DEFINE([HAVE_PTHREAD_RWLOCK], [1], + [Define if the POSIX multithreading library has read/write locks.])], + [], + [#include ]) + # glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro. + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM( + [[#include ]], + [[ +#if __FreeBSD__ == 4 +error "No, in FreeBSD 4.0 recursive mutexes actually don't work." +#elif (defined __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ \ + && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) +error "No, in Mac OS X < 10.7 recursive mutexes actually don't work." +#else +int x = (int)PTHREAD_MUTEX_RECURSIVE; +return !x; +#endif + ]])], + [AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE], [1], + [Define if the defines PTHREAD_MUTEX_RECURSIVE.])]) + fi + gl_PREREQ_LOCK +]) + +# Prerequisites of lib/glthread/lock.c. +AC_DEFUN([gl_PREREQ_LOCK], [:]) diff --git a/m4/longlong.m4 b/m4/longlong.m4 new file mode 100644 index 00000000..d57bc700 --- /dev/null +++ b/m4/longlong.m4 @@ -0,0 +1,113 @@ +# longlong.m4 serial 17 +dnl Copyright (C) 1999-2007, 2009-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +# Define HAVE_LONG_LONG_INT if 'long long int' works. +# This fixes a bug in Autoconf 2.61, and can be faster +# than what's in Autoconf 2.62 through 2.68. + +# Note: If the type 'long long int' exists but is only 32 bits large +# (as on some very old compilers), HAVE_LONG_LONG_INT will not be +# defined. In this case you can treat 'long long int' like 'long int'. + +AC_DEFUN([AC_TYPE_LONG_LONG_INT], +[ + AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) + AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int], + [ac_cv_type_long_long_int=yes + if test "x${ac_cv_prog_cc_c99-no}" = xno; then + ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int + if test $ac_cv_type_long_long_int = yes; then + dnl Catch a bug in Tandem NonStop Kernel (OSS) cc -O circa 2004. + dnl If cross compiling, assume the bug is not important, since + dnl nobody cross compiles for this platform as far as we know. + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[@%:@include + @%:@ifndef LLONG_MAX + @%:@ define HALF \ + (1LL << (sizeof (long long int) * CHAR_BIT - 2)) + @%:@ define LLONG_MAX (HALF - 1 + HALF) + @%:@endif]], + [[long long int n = 1; + int i; + for (i = 0; ; i++) + { + long long int m = n << i; + if (m >> i != n) + return 1; + if (LLONG_MAX / 2 < m) + break; + } + return 0;]])], + [], + [ac_cv_type_long_long_int=no], + [:]) + fi + fi]) + if test $ac_cv_type_long_long_int = yes; then + AC_DEFINE([HAVE_LONG_LONG_INT], [1], + [Define to 1 if the system has the type 'long long int'.]) + fi +]) + +# Define HAVE_UNSIGNED_LONG_LONG_INT if 'unsigned long long int' works. +# This fixes a bug in Autoconf 2.61, and can be faster +# than what's in Autoconf 2.62 through 2.68. + +# Note: If the type 'unsigned long long int' exists but is only 32 bits +# large (as on some very old compilers), AC_TYPE_UNSIGNED_LONG_LONG_INT +# will not be defined. In this case you can treat 'unsigned long long int' +# like 'unsigned long int'. + +AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], +[ + AC_CACHE_CHECK([for unsigned long long int], + [ac_cv_type_unsigned_long_long_int], + [ac_cv_type_unsigned_long_long_int=yes + if test "x${ac_cv_prog_cc_c99-no}" = xno; then + AC_LINK_IFELSE( + [_AC_TYPE_LONG_LONG_SNIPPET], + [], + [ac_cv_type_unsigned_long_long_int=no]) + fi]) + if test $ac_cv_type_unsigned_long_long_int = yes; then + AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], [1], + [Define to 1 if the system has the type 'unsigned long long int'.]) + fi +]) + +# Expands to a C program that can be used to test for simultaneous support +# of 'long long' and 'unsigned long long'. We don't want to say that +# 'long long' is available if 'unsigned long long' is not, or vice versa, +# because too many programs rely on the symmetry between signed and unsigned +# integer types (excluding 'bool'). +AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET], +[ + AC_LANG_PROGRAM( + [[/* For now, do not test the preprocessor; as of 2007 there are too many + implementations with broken preprocessors. Perhaps this can + be revisited in 2012. In the meantime, code should not expect + #if to work with literals wider than 32 bits. */ + /* Test literals. */ + long long int ll = 9223372036854775807ll; + long long int nll = -9223372036854775807LL; + unsigned long long int ull = 18446744073709551615ULL; + /* Test constant expressions. */ + typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) + ? 1 : -1)]; + typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 + ? 1 : -1)]; + int i = 63;]], + [[/* Test availability of runtime routines for shift and division. */ + long long int llmax = 9223372036854775807ll; + unsigned long long int ullmax = 18446744073709551615ull; + return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) + | (llmax / ll) | (llmax % ll) + | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) + | (ullmax / ull) | (ullmax % ull));]]) +]) diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4 new file mode 100644 index 00000000..94b08297 --- /dev/null +++ b/m4/ltoptions.m4 @@ -0,0 +1,437 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 8 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option '$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl 'shared' nor 'disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], + [_LT_WITH_AIX_SONAME([aix])]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the 'shared' and +# 'disable-shared' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the 'static' and +# 'disable-static' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the 'fast-install' +# and 'disable-fast-install' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_AIX_SONAME([DEFAULT]) +# ---------------------------------- +# implement the --with-aix-soname flag, and support the `aix-soname=aix' +# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT +# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. +m4_define([_LT_WITH_AIX_SONAME], +[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl +shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[[5-9]]*,yes) + AC_MSG_CHECKING([which variant of shared library versioning to provide]) + AC_ARG_WITH([aix-soname], + [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], + [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], + [case $withval in + aix|svr4|both) + ;; + *) + AC_MSG_ERROR([Unknown argument to --with-aix-soname]) + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname], + [AC_CACHE_VAL([lt_cv_with_aix_soname], + [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) + with_aix_soname=$lt_cv_with_aix_soname]) + AC_MSG_RESULT([$with_aix_soname]) + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + +_LT_DECL([], [shared_archive_member_spec], [0], + [Shared archive member basename, for filename based shared library versioning on AIX])dnl +])# _LT_WITH_AIX_SONAME + +LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the 'pic-only' and 'no-pic' +# LT_INIT options. +# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [pic_mode=m4_default([$1], [default])]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4 new file mode 100644 index 00000000..48bc9344 --- /dev/null +++ b/m4/ltsugar.m4 @@ -0,0 +1,124 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59, which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) diff --git a/m4/ltversion.m4 b/m4/ltversion.m4 new file mode 100644 index 00000000..fa04b52a --- /dev/null +++ b/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 4179 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.6]) +m4_define([LT_PACKAGE_REVISION], [2.4.6]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.6' +macro_revision='2.4.6' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4 new file mode 100644 index 00000000..c6b26f88 --- /dev/null +++ b/m4/lt~obsolete.m4 @@ -0,0 +1,99 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/m4/nls.m4 b/m4/nls.m4 new file mode 100644 index 00000000..93df8d3b --- /dev/null +++ b/m4/nls.m4 @@ -0,0 +1,32 @@ +# nls.m4 serial 5 (gettext-0.18) +dnl Copyright (C) 1995-2003, 2005-2006, 2008-2014 Free Software Foundation, +dnl Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995-2000. +dnl Bruno Haible , 2000-2003. + +AC_PREREQ([2.50]) + +AC_DEFUN([AM_NLS], +[ + AC_MSG_CHECKING([whether NLS is requested]) + dnl Default is enabled NLS + AC_ARG_ENABLE([nls], + [ --disable-nls do not use Native Language Support], + USE_NLS=$enableval, USE_NLS=yes) + AC_MSG_RESULT([$USE_NLS]) + AC_SUBST([USE_NLS]) +]) diff --git a/m4/pkg.m4 b/m4/pkg.m4 new file mode 100644 index 00000000..c5b26b52 --- /dev/null +++ b/m4/pkg.m4 @@ -0,0 +1,214 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 1 (pkg-config-0.24) +# +# Copyright © 2004 Scott James Remnant . +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +# only at the first occurence in configure.ac, so if the first place +# it's called might be skipped (such as if it is within an "if", you +# have to call PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])# PKG_CHECK_MODULES + + +# PKG_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable pkgconfigdir as the location where a module +# should install pkg-config .pc files. By default the directory is +# $libdir/pkgconfig, but the default can be changed by passing +# DIRECTORY. The user can override through the --with-pkgconfigdir +# parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_INSTALLDIR + + +# PKG_NOARCH_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable noarch_pkgconfigdir as the location where a +# module should install arch-independent pkg-config .pc files. By +# default the directory is $datadir/pkgconfig, but the default can be +# changed by passing DIRECTORY. The user can override through the +# --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_NOARCH_INSTALLDIR + + +# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# ------------------------------------------- +# Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])# PKG_CHECK_VAR diff --git a/m4/po.m4 b/m4/po.m4 new file mode 100644 index 00000000..d4bc2627 --- /dev/null +++ b/m4/po.m4 @@ -0,0 +1,453 @@ +# po.m4 serial 24 (gettext-0.19) +dnl Copyright (C) 1995-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1995-2000. +dnl Bruno Haible , 2000-2003. + +AC_PREREQ([2.60]) + +dnl Checks for all prerequisites of the po subdirectory. +AC_DEFUN([AM_PO_SUBDIRS], +[ + AC_REQUIRE([AC_PROG_MAKE_SET])dnl + AC_REQUIRE([AC_PROG_INSTALL])dnl + AC_REQUIRE([AC_PROG_MKDIR_P])dnl + AC_REQUIRE([AC_PROG_SED])dnl + AC_REQUIRE([AM_NLS])dnl + + dnl Release version of the gettext macros. This is used to ensure that + dnl the gettext macros and po/Makefile.in.in are in sync. + AC_SUBST([GETTEXT_MACRO_VERSION], [0.19]) + + dnl Perform the following tests also if --disable-nls has been given, + dnl because they are needed for "make dist" to work. + + dnl Search for GNU msgfmt in the PATH. + dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions. + dnl The second test excludes FreeBSD msgfmt. + AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, + [$ac_dir/$ac_word --statistics /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 && + (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], + :) + AC_PATH_PROG([GMSGFMT], [gmsgfmt], [$MSGFMT]) + + dnl Test whether it is GNU msgfmt >= 0.15. +changequote(,)dnl + case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;; + *) MSGFMT_015=$MSGFMT ;; + esac +changequote([,])dnl + AC_SUBST([MSGFMT_015]) +changequote(,)dnl + case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;; + *) GMSGFMT_015=$GMSGFMT ;; + esac +changequote([,])dnl + AC_SUBST([GMSGFMT_015]) + + dnl Search for GNU xgettext 0.12 or newer in the PATH. + dnl The first test excludes Solaris xgettext and early GNU xgettext versions. + dnl The second test excludes FreeBSD xgettext. + AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, + [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 && + (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], + :) + dnl Remove leftover from FreeBSD xgettext call. + rm -f messages.po + + dnl Test whether it is GNU xgettext >= 0.15. +changequote(,)dnl + case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;; + *) XGETTEXT_015=$XGETTEXT ;; + esac +changequote([,])dnl + AC_SUBST([XGETTEXT_015]) + + dnl Search for GNU msgmerge 0.11 or newer in the PATH. + AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge, + [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1], :) + + dnl Installation directories. + dnl Autoconf >= 2.60 defines localedir. For older versions of autoconf, we + dnl have to define it here, so that it can be used in po/Makefile. + test -n "$localedir" || localedir='${datadir}/locale' + AC_SUBST([localedir]) + + dnl Support for AM_XGETTEXT_OPTION. + test -n "${XGETTEXT_EXTRA_OPTIONS+set}" || XGETTEXT_EXTRA_OPTIONS= + AC_SUBST([XGETTEXT_EXTRA_OPTIONS]) + + AC_CONFIG_COMMANDS([po-directories], [[ + for ac_file in $CONFIG_FILES; do + # Support "outfile[:infile[:infile...]]" + case "$ac_file" in + *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + esac + # PO directories have a Makefile.in generated from Makefile.in.in. + case "$ac_file" in */Makefile.in) + # Adjust a relative srcdir. + ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` + ac_dir_suffix=/`echo "$ac_dir"|sed 's%^\./%%'` + ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` + # In autoconf-2.13 it is called $ac_given_srcdir. + # In autoconf-2.50 it is called $srcdir. + test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" + case "$ac_given_srcdir" in + .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; + /*) top_srcdir="$ac_given_srcdir" ;; + *) top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + # Treat a directory as a PO directory if and only if it has a + # POTFILES.in file. This allows packages to have multiple PO + # directories under different names or in different locations. + if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then + rm -f "$ac_dir/POTFILES" + test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES" + gt_tab=`printf '\t'` + cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ${gt_tab}]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES" + POMAKEFILEDEPS="POTFILES.in" + # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend + # on $ac_dir but don't depend on user-specified configuration + # parameters. + if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then + # The LINGUAS file contains the set of available languages. + if test -n "$OBSOLETE_ALL_LINGUAS"; then + test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete" + fi + ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"` + # Hide the ALL_LINGUAS assignment from automake < 1.5. + eval 'ALL_LINGUAS''=$ALL_LINGUAS_' + POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS" + else + # The set of available languages was given in configure.in. + # Hide the ALL_LINGUAS assignment from automake < 1.5. + eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS' + fi + # Compute POFILES + # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po) + # Compute UPDATEPOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update) + # Compute DUMMYPOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop) + # Compute GMOFILES + # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo) + case "$ac_given_srcdir" in + .) srcdirpre= ;; + *) srcdirpre='$(srcdir)/' ;; + esac + POFILES= + UPDATEPOFILES= + DUMMYPOFILES= + GMOFILES= + for lang in $ALL_LINGUAS; do + POFILES="$POFILES $srcdirpre$lang.po" + UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" + DUMMYPOFILES="$DUMMYPOFILES $lang.nop" + GMOFILES="$GMOFILES $srcdirpre$lang.gmo" + done + # CATALOGS depends on both $ac_dir and the user's LINGUAS + # environment variable. + INST_LINGUAS= + if test -n "$ALL_LINGUAS"; then + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "$LINGUAS"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + INST_LINGUAS="$INST_LINGUAS $presentlang" + fi + done + fi + CATALOGS= + if test -n "$INST_LINGUAS"; then + for lang in $INST_LINGUAS; do + CATALOGS="$CATALOGS $lang.gmo" + done + fi + test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile" + sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile" + for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do + if test -f "$f"; then + case "$f" in + *.orig | *.bak | *~) ;; + *) cat "$f" >> "$ac_dir/Makefile" ;; + esac + fi + done + fi + ;; + esac + done]], + [# Capture the value of obsolete ALL_LINGUAS because we need it to compute + # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it + # from automake < 1.5. + eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"' + # Capture the value of LINGUAS because we need it to compute CATALOGS. + LINGUAS="${LINGUAS-%UNSET%}" + ]) +]) + +dnl Postprocesses a Makefile in a directory containing PO files. +AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE], +[ + # When this code is run, in config.status, two variables have already been + # set: + # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in, + # - LINGUAS is the value of the environment variable LINGUAS at configure + # time. + +changequote(,)dnl + # Adjust a relative srcdir. + ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` + ac_dir_suffix=/`echo "$ac_dir"|sed 's%^\./%%'` + ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` + # In autoconf-2.13 it is called $ac_given_srcdir. + # In autoconf-2.50 it is called $srcdir. + test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" + case "$ac_given_srcdir" in + .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; + /*) top_srcdir="$ac_given_srcdir" ;; + *) top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + # Find a way to echo strings without interpreting backslash. + if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then + gt_echo='echo' + else + if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then + gt_echo='printf %s\n' + else + echo_func () { + cat < "$ac_file.tmp" + tab=`printf '\t'` + if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then + # Add dependencies that cannot be formulated as a simple suffix rule. + for lang in $ALL_LINGUAS; do + frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` + cat >> "$ac_file.tmp" < /dev/null; then + # Add dependencies that cannot be formulated as a simple suffix rule. + for lang in $ALL_LINGUAS; do + frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'` + cat >> "$ac_file.tmp" <> "$ac_file.tmp" < +#include +/* The string "%2$d %1$d", with dollar characters protected from the shell's + dollar expansion (possibly an autoconf bug). */ +static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' }; +static char buf[100]; +int main () +{ + sprintf (buf, format, 33, 55); + return (strcmp (buf, "55 33") != 0); +}]])], + [gt_cv_func_printf_posix=yes], + [gt_cv_func_printf_posix=no], + [ + AC_EGREP_CPP([notposix], [ +#if defined __NetBSD__ || defined __BEOS__ || defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ + notposix +#endif + ], + [gt_cv_func_printf_posix="guessing no"], + [gt_cv_func_printf_posix="guessing yes"]) + ]) + ]) + case $gt_cv_func_printf_posix in + *yes) + AC_DEFINE([HAVE_POSIX_PRINTF], [1], + [Define if your printf() function supports format strings with positions.]) + ;; + esac +]) diff --git a/m4/progtest.m4 b/m4/progtest.m4 new file mode 100644 index 00000000..0921e1e5 --- /dev/null +++ b/m4/progtest.m4 @@ -0,0 +1,91 @@ +# progtest.m4 serial 7 (gettext-0.18.2) +dnl Copyright (C) 1996-2003, 2005, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Authors: +dnl Ulrich Drepper , 1996. + +AC_PREREQ([2.50]) + +# Search path for a program which passes the given test. + +dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, +dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) +AC_DEFUN([AM_PATH_PROG_WITH_TEST], +[ +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + # Determine PATH_SEPARATOR by trying to find /bin/sh in a PATH which + # contains only /bin. Note that ksh looks also at the FPATH variable, + # so we have to set that as well for the test. + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ + && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \ + || PATH_SEPARATOR=';' + } +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "$2", so it can be a program name with args. +set dummy $2; ac_word=[$]2 +AC_MSG_CHECKING([for $ac_word]) +AC_CACHE_VAL([ac_cv_path_$1], +[case "[$]$1" in + [[\\/]]* | ?:[[\\/]]*) + ac_cv_path_$1="[$]$1" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in ifelse([$5], , $PATH, [$5]); do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD + if [$3]; then + ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" +dnl If no 4th arg is given, leave the cache variable unset, +dnl so AC_PATH_PROGS will keep looking. +ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" +])dnl + ;; +esac])dnl +$1="$ac_cv_path_$1" +if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then + AC_MSG_RESULT([$][$1]) +else + AC_MSG_RESULT([no]) +fi +AC_SUBST([$1])dnl +]) diff --git a/m4/size_max.m4 b/m4/size_max.m4 new file mode 100644 index 00000000..186e3fdd --- /dev/null +++ b/m4/size_max.m4 @@ -0,0 +1,79 @@ +# size_max.m4 serial 10 +dnl Copyright (C) 2003, 2005-2006, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([gl_SIZE_MAX], +[ + AC_CHECK_HEADERS([stdint.h]) + dnl First test whether the system already has SIZE_MAX. + AC_CACHE_CHECK([for SIZE_MAX], [gl_cv_size_max], [ + gl_cv_size_max= + AC_EGREP_CPP([Found it], [ +#include +#if HAVE_STDINT_H +#include +#endif +#ifdef SIZE_MAX +Found it +#endif +], [gl_cv_size_max=yes]) + if test -z "$gl_cv_size_max"; then + dnl Define it ourselves. Here we assume that the type 'size_t' is not wider + dnl than the type 'unsigned long'. Try hard to find a definition that can + dnl be used in a preprocessor #if, i.e. doesn't contain a cast. + AC_COMPUTE_INT([size_t_bits_minus_1], [sizeof (size_t) * CHAR_BIT - 1], + [#include +#include ], [size_t_bits_minus_1=]) + AC_COMPUTE_INT([fits_in_uint], [sizeof (size_t) <= sizeof (unsigned int)], + [#include ], [fits_in_uint=]) + if test -n "$size_t_bits_minus_1" && test -n "$fits_in_uint"; then + if test $fits_in_uint = 1; then + dnl Even though SIZE_MAX fits in an unsigned int, it must be of type + dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'. + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + extern size_t foo; + extern unsigned long foo; + ]], + [[]])], + [fits_in_uint=0]) + fi + dnl We cannot use 'expr' to simplify this expression, because 'expr' + dnl works only with 'long' integers in the host environment, while we + dnl might be cross-compiling from a 32-bit platform to a 64-bit platform. + if test $fits_in_uint = 1; then + gl_cv_size_max="(((1U << $size_t_bits_minus_1) - 1) * 2 + 1)" + else + gl_cv_size_max="(((1UL << $size_t_bits_minus_1) - 1) * 2 + 1)" + fi + else + dnl Shouldn't happen, but who knows... + gl_cv_size_max='((size_t)~(size_t)0)' + fi + fi + ]) + if test "$gl_cv_size_max" != yes; then + AC_DEFINE_UNQUOTED([SIZE_MAX], [$gl_cv_size_max], + [Define as the maximum value of type 'size_t', if the system doesn't define it.]) + fi + dnl Don't redefine SIZE_MAX in config.h if config.h is re-included after + dnl . Remember that the #undef in AH_VERBATIM gets replaced with + dnl #define by AC_DEFINE_UNQUOTED. + AH_VERBATIM([SIZE_MAX], +[/* Define as the maximum value of type 'size_t', if the system doesn't define + it. */ +#ifndef SIZE_MAX +# undef SIZE_MAX +#endif]) +]) + +dnl Autoconf >= 2.61 has AC_COMPUTE_INT built-in. +dnl Remove this when we can assume autoconf >= 2.61. +m4_ifdef([AC_COMPUTE_INT], [], [ + AC_DEFUN([AC_COMPUTE_INT], [_AC_COMPUTE_INT([$2],[$1],[$3],[$4])]) +]) diff --git a/m4/stdint_h.m4 b/m4/stdint_h.m4 new file mode 100644 index 00000000..5097c0b0 --- /dev/null +++ b/m4/stdint_h.m4 @@ -0,0 +1,27 @@ +# stdint_h.m4 serial 9 +dnl Copyright (C) 1997-2004, 2006, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +# Define HAVE_STDINT_H_WITH_UINTMAX if exists, +# doesn't clash with , and declares uintmax_t. + +AC_DEFUN([gl_AC_HEADER_STDINT_H], +[ + AC_CACHE_CHECK([for stdint.h], [gl_cv_header_stdint_h], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include ]], + [[uintmax_t i = (uintmax_t) -1; return !i;]])], + [gl_cv_header_stdint_h=yes], + [gl_cv_header_stdint_h=no])]) + if test $gl_cv_header_stdint_h = yes; then + AC_DEFINE_UNQUOTED([HAVE_STDINT_H_WITH_UINTMAX], [1], + [Define if exists, doesn't clash with , + and declares uintmax_t. ]) + fi +]) diff --git a/m4/threadlib.m4 b/m4/threadlib.m4 new file mode 100644 index 00000000..8615ac94 --- /dev/null +++ b/m4/threadlib.m4 @@ -0,0 +1,389 @@ +# threadlib.m4 serial 11 (gettext-0.18.2) +dnl Copyright (C) 2005-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl gl_THREADLIB +dnl ------------ +dnl Tests for a multithreading library to be used. +dnl If the configure.ac contains a definition of the gl_THREADLIB_DEFAULT_NO +dnl (it must be placed before the invocation of gl_THREADLIB_EARLY!), then the +dnl default is 'no', otherwise it is system dependent. In both cases, the user +dnl can change the choice through the options --enable-threads=choice or +dnl --disable-threads. +dnl Defines at most one of the macros USE_POSIX_THREADS, USE_SOLARIS_THREADS, +dnl USE_PTH_THREADS, USE_WINDOWS_THREADS +dnl Sets the variables LIBTHREAD and LTLIBTHREAD to the linker options for use +dnl in a Makefile (LIBTHREAD for use without libtool, LTLIBTHREAD for use with +dnl libtool). +dnl Sets the variables LIBMULTITHREAD and LTLIBMULTITHREAD similarly, for +dnl programs that really need multithread functionality. The difference +dnl between LIBTHREAD and LIBMULTITHREAD is that on platforms supporting weak +dnl symbols, typically LIBTHREAD is empty whereas LIBMULTITHREAD is not. +dnl Adds to CPPFLAGS the flag -D_REENTRANT or -D_THREAD_SAFE if needed for +dnl multithread-safe programs. + +AC_DEFUN([gl_THREADLIB_EARLY], +[ + AC_REQUIRE([gl_THREADLIB_EARLY_BODY]) +]) + +dnl The guts of gl_THREADLIB_EARLY. Needs to be expanded only once. + +AC_DEFUN([gl_THREADLIB_EARLY_BODY], +[ + dnl Ordering constraints: This macro modifies CPPFLAGS in a way that + dnl influences the result of the autoconf tests that test for *_unlocked + dnl declarations, on AIX 5 at least. Therefore it must come early. + AC_BEFORE([$0], [gl_FUNC_GLIBC_UNLOCKED_IO])dnl + AC_BEFORE([$0], [gl_ARGP])dnl + + AC_REQUIRE([AC_CANONICAL_HOST]) + dnl _GNU_SOURCE is needed for pthread_rwlock_t on glibc systems. + dnl AC_USE_SYSTEM_EXTENSIONS was introduced in autoconf 2.60 and obsoletes + dnl AC_GNU_SOURCE. + m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], + [AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])], + [AC_REQUIRE([AC_GNU_SOURCE])]) + dnl Check for multithreading. + m4_ifdef([gl_THREADLIB_DEFAULT_NO], + [m4_divert_text([DEFAULTS], [gl_use_threads_default=no])], + [m4_divert_text([DEFAULTS], [gl_use_threads_default=])]) + AC_ARG_ENABLE([threads], +AC_HELP_STRING([--enable-threads={posix|solaris|pth|windows}], [specify multithreading API])m4_ifdef([gl_THREADLIB_DEFAULT_NO], [], [ +AC_HELP_STRING([--disable-threads], [build without multithread safety])]), + [gl_use_threads=$enableval], + [if test -n "$gl_use_threads_default"; then + gl_use_threads="$gl_use_threads_default" + else +changequote(,)dnl + case "$host_os" in + dnl Disable multithreading by default on OSF/1, because it interferes + dnl with fork()/exec(): When msgexec is linked with -lpthread, its + dnl child process gets an endless segmentation fault inside execvp(). + dnl Disable multithreading by default on Cygwin 1.5.x, because it has + dnl bugs that lead to endless loops or crashes. See + dnl . + osf*) gl_use_threads=no ;; + cygwin*) + case `uname -r` in + 1.[0-5].*) gl_use_threads=no ;; + *) gl_use_threads=yes ;; + esac + ;; + *) gl_use_threads=yes ;; + esac +changequote([,])dnl + fi + ]) + if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then + # For using : + case "$host_os" in + osf*) + # On OSF/1, the compiler needs the flag -D_REENTRANT so that it + # groks . cc also understands the flag -pthread, but + # we don't use it because 1. gcc-2.95 doesn't understand -pthread, + # 2. putting a flag into CPPFLAGS that has an effect on the linker + # causes the AC_LINK_IFELSE test below to succeed unexpectedly, + # leading to wrong values of LIBTHREAD and LTLIBTHREAD. + CPPFLAGS="$CPPFLAGS -D_REENTRANT" + ;; + esac + # Some systems optimize for single-threaded programs by default, and + # need special flags to disable these optimizations. For example, the + # definition of 'errno' in . + case "$host_os" in + aix* | freebsd*) CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE" ;; + solaris*) CPPFLAGS="$CPPFLAGS -D_REENTRANT" ;; + esac + fi +]) + +dnl The guts of gl_THREADLIB. Needs to be expanded only once. + +AC_DEFUN([gl_THREADLIB_BODY], +[ + AC_REQUIRE([gl_THREADLIB_EARLY_BODY]) + gl_threads_api=none + LIBTHREAD= + LTLIBTHREAD= + LIBMULTITHREAD= + LTLIBMULTITHREAD= + if test "$gl_use_threads" != no; then + dnl Check whether the compiler and linker support weak declarations. + AC_CACHE_CHECK([whether imported symbols can be declared weak], + [gl_cv_have_weak], + [gl_cv_have_weak=no + dnl First, test whether the compiler accepts it syntactically. + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern void xyzzy (); +#pragma weak xyzzy]], + [[xyzzy();]])], + [gl_cv_have_weak=maybe]) + if test $gl_cv_have_weak = maybe; then + dnl Second, test whether it actually works. On Cygwin 1.7.2, with + dnl gcc 4.3, symbols declared weak always evaluate to the address 0. + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#pragma weak fputs +int main () +{ + return (fputs == NULL); +}]])], + [gl_cv_have_weak=yes], + [gl_cv_have_weak=no], + [dnl When cross-compiling, assume that only ELF platforms support + dnl weak symbols. + AC_EGREP_CPP([Extensible Linking Format], + [#ifdef __ELF__ + Extensible Linking Format + #endif + ], + [gl_cv_have_weak="guessing yes"], + [gl_cv_have_weak="guessing no"]) + ]) + fi + ]) + if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then + # On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that + # it groks . It's added above, in gl_THREADLIB_EARLY_BODY. + AC_CHECK_HEADER([pthread.h], + [gl_have_pthread_h=yes], [gl_have_pthread_h=no]) + if test "$gl_have_pthread_h" = yes; then + # Other possible tests: + # -lpthreads (FSU threads, PCthreads) + # -lgthreads + gl_have_pthread= + # Test whether both pthread_mutex_lock and pthread_mutexattr_init exist + # in libc. IRIX 6.5 has the first one in both libc and libpthread, but + # the second one only in libpthread, and lock.c needs it. + # + # If -pthread works, prefer it to -lpthread, since Ubuntu 14.04 + # needs -pthread for some reason. See: + # http://lists.gnu.org/archive/html/bug-gnulib/2014-09/msg00023.html + save_LIBS=$LIBS + for gl_pthread in '' '-pthread'; do + LIBS="$LIBS $gl_pthread" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include + pthread_mutex_t m; + pthread_mutexattr_t ma; + ]], + [[pthread_mutex_lock (&m); + pthread_mutexattr_init (&ma);]])], + [gl_have_pthread=yes + LIBTHREAD=$gl_pthread LTLIBTHREAD=$gl_pthread + LIBMULTITHREAD=$gl_pthread LTLIBMULTITHREAD=$gl_pthread]) + LIBS=$save_LIBS + test -n "$gl_have_pthread" && break + done + + # Test for libpthread by looking for pthread_kill. (Not pthread_self, + # since it is defined as a macro on OSF/1.) + if test -n "$gl_have_pthread" && test -z "$LIBTHREAD"; then + # The program links fine without libpthread. But it may actually + # need to link with libpthread in order to create multiple threads. + AC_CHECK_LIB([pthread], [pthread_kill], + [LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread + # On Solaris and HP-UX, most pthread functions exist also in libc. + # Therefore pthread_in_use() needs to actually try to create a + # thread: pthread_create from libc will fail, whereas + # pthread_create will actually create a thread. + case "$host_os" in + solaris* | hpux*) + AC_DEFINE([PTHREAD_IN_USE_DETECTION_HARD], [1], + [Define if the pthread_in_use() detection is hard.]) + esac + ]) + elif test -z "$gl_have_pthread"; then + # Some library is needed. Try libpthread and libc_r. + AC_CHECK_LIB([pthread], [pthread_kill], + [gl_have_pthread=yes + LIBTHREAD=-lpthread LTLIBTHREAD=-lpthread + LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread]) + if test -z "$gl_have_pthread"; then + # For FreeBSD 4. + AC_CHECK_LIB([c_r], [pthread_kill], + [gl_have_pthread=yes + LIBTHREAD=-lc_r LTLIBTHREAD=-lc_r + LIBMULTITHREAD=-lc_r LTLIBMULTITHREAD=-lc_r]) + fi + fi + if test -n "$gl_have_pthread"; then + gl_threads_api=posix + AC_DEFINE([USE_POSIX_THREADS], [1], + [Define if the POSIX multithreading library can be used.]) + if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then + if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then + AC_DEFINE([USE_POSIX_THREADS_WEAK], [1], + [Define if references to the POSIX multithreading library should be made weak.]) + LIBTHREAD= + LTLIBTHREAD= + fi + fi + fi + fi + fi + if test -z "$gl_have_pthread"; then + if test "$gl_use_threads" = yes || test "$gl_use_threads" = solaris; then + gl_have_solaristhread= + gl_save_LIBS="$LIBS" + LIBS="$LIBS -lthread" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include +#include + ]], + [[thr_self();]])], + [gl_have_solaristhread=yes]) + LIBS="$gl_save_LIBS" + if test -n "$gl_have_solaristhread"; then + gl_threads_api=solaris + LIBTHREAD=-lthread + LTLIBTHREAD=-lthread + LIBMULTITHREAD="$LIBTHREAD" + LTLIBMULTITHREAD="$LTLIBTHREAD" + AC_DEFINE([USE_SOLARIS_THREADS], [1], + [Define if the old Solaris multithreading library can be used.]) + if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then + AC_DEFINE([USE_SOLARIS_THREADS_WEAK], [1], + [Define if references to the old Solaris multithreading library should be made weak.]) + LIBTHREAD= + LTLIBTHREAD= + fi + fi + fi + fi + if test "$gl_use_threads" = pth; then + gl_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_LINKFLAGS([pth]) + gl_have_pth= + gl_save_LIBS="$LIBS" + LIBS="$LIBS $LIBPTH" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[#include ]], [[pth_self();]])], + [gl_have_pth=yes]) + LIBS="$gl_save_LIBS" + if test -n "$gl_have_pth"; then + gl_threads_api=pth + LIBTHREAD="$LIBPTH" + LTLIBTHREAD="$LTLIBPTH" + LIBMULTITHREAD="$LIBTHREAD" + LTLIBMULTITHREAD="$LTLIBTHREAD" + AC_DEFINE([USE_PTH_THREADS], [1], + [Define if the GNU Pth multithreading library can be used.]) + if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then + if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then + AC_DEFINE([USE_PTH_THREADS_WEAK], [1], + [Define if references to the GNU Pth multithreading library should be made weak.]) + LIBTHREAD= + LTLIBTHREAD= + fi + fi + else + CPPFLAGS="$gl_save_CPPFLAGS" + fi + fi + if test -z "$gl_have_pthread"; then + case "$gl_use_threads" in + yes | windows | win32) # The 'win32' is for backward compatibility. + if { case "$host_os" in + mingw*) true;; + *) false;; + esac + }; then + gl_threads_api=windows + AC_DEFINE([USE_WINDOWS_THREADS], [1], + [Define if the native Windows multithreading API can be used.]) + fi + ;; + esac + fi + fi + AC_MSG_CHECKING([for multithread API to use]) + AC_MSG_RESULT([$gl_threads_api]) + AC_SUBST([LIBTHREAD]) + AC_SUBST([LTLIBTHREAD]) + AC_SUBST([LIBMULTITHREAD]) + AC_SUBST([LTLIBMULTITHREAD]) +]) + +AC_DEFUN([gl_THREADLIB], +[ + AC_REQUIRE([gl_THREADLIB_EARLY]) + AC_REQUIRE([gl_THREADLIB_BODY]) +]) + + +dnl gl_DISABLE_THREADS +dnl ------------------ +dnl Sets the gl_THREADLIB default so that threads are not used by default. +dnl The user can still override it at installation time, by using the +dnl configure option '--enable-threads'. + +AC_DEFUN([gl_DISABLE_THREADS], [ + m4_divert_text([INIT_PREPARE], [gl_use_threads_default=no]) +]) + + +dnl Survey of platforms: +dnl +dnl Platform Available Compiler Supports test-lock +dnl flavours option weak result +dnl --------------- --------- --------- -------- --------- +dnl Linux 2.4/glibc posix -lpthread Y OK +dnl +dnl GNU Hurd/glibc posix +dnl +dnl Ubuntu 14.04 posix -pthread Y OK +dnl +dnl FreeBSD 5.3 posix -lc_r Y +dnl posix -lkse ? Y +dnl posix -lpthread ? Y +dnl posix -lthr Y +dnl +dnl FreeBSD 5.2 posix -lc_r Y +dnl posix -lkse Y +dnl posix -lthr Y +dnl +dnl FreeBSD 4.0,4.10 posix -lc_r Y OK +dnl +dnl NetBSD 1.6 -- +dnl +dnl OpenBSD 3.4 posix -lpthread Y OK +dnl +dnl Mac OS X 10.[123] posix -lpthread Y OK +dnl +dnl Solaris 7,8,9 posix -lpthread Y Sol 7,8: 0.0; Sol 9: OK +dnl solaris -lthread Y Sol 7,8: 0.0; Sol 9: OK +dnl +dnl HP-UX 11 posix -lpthread N (cc) OK +dnl Y (gcc) +dnl +dnl IRIX 6.5 posix -lpthread Y 0.5 +dnl +dnl AIX 4.3,5.1 posix -lpthread N AIX 4: 0.5; AIX 5: OK +dnl +dnl OSF/1 4.0,5.1 posix -pthread (cc) N OK +dnl -lpthread (gcc) Y +dnl +dnl Cygwin posix -lpthread Y OK +dnl +dnl Any of the above pth -lpth 0.0 +dnl +dnl Mingw windows N OK +dnl +dnl BeOS 5 -- +dnl +dnl The test-lock result shows what happens if in test-lock.c EXPLICIT_YIELD is +dnl turned off: +dnl OK if all three tests terminate OK, +dnl 0.5 if the first test terminates OK but the second one loops endlessly, +dnl 0.0 if the first test already loops endlessly. diff --git a/m4/uintmax_t.m4 b/m4/uintmax_t.m4 new file mode 100644 index 00000000..8a92f58e --- /dev/null +++ b/m4/uintmax_t.m4 @@ -0,0 +1,30 @@ +# uintmax_t.m4 serial 12 +dnl Copyright (C) 1997-2004, 2007-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +AC_PREREQ([2.13]) + +# Define uintmax_t to 'unsigned long' or 'unsigned long long' +# if it is not already defined in or . + +AC_DEFUN([gl_AC_TYPE_UINTMAX_T], +[ + AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) + AC_REQUIRE([gl_AC_HEADER_STDINT_H]) + if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then + AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) + test $ac_cv_type_unsigned_long_long_int = yes \ + && ac_type='unsigned long long' \ + || ac_type='unsigned long' + AC_DEFINE_UNQUOTED([uintmax_t], [$ac_type], + [Define to unsigned long or unsigned long long + if and don't define.]) + else + AC_DEFINE([HAVE_UINTMAX_T], [1], + [Define if you have the 'uintmax_t' type in or .]) + fi +]) diff --git a/m4/vala.m4 b/m4/vala.m4 new file mode 100644 index 00000000..4393866d --- /dev/null +++ b/m4/vala.m4 @@ -0,0 +1,135 @@ +dnl vala.m4 +dnl +dnl Copyright 2010 Marc-Andre Lureau +dnl Copyright 2011 Rodney Dawes +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License, or (at your option) any later version. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# _VALA_CHECK_COMPILE_WITH_ARGS(ARGS, [ACTION-IF-TRUE], +# [ACTION-IF-FALSE]) +# -------------------------------------- +# Check that Vala compile with ARGS. +# +AC_DEFUN([_VALA_CHECK_COMPILE_WITH_ARGS], +[AC_REQUIRE([AM_PROG_VALAC])[]dnl + + cat <<_ACEOF >conftest.vala +void main(){} +_ACEOF + + AS_IF([vala_error=`$VALAC $1 -q --cc="${CC} ${CPPFLAGS} ${CFLAGS} ${LDFLAGS}" -o conftest$ac_exeext conftest.vala 2>&1`], + [$2], [$3]) +]) + +])# _VALA_CHECK_COMPILE_WITH_ARGS + +# VALA_CHECK_PACKAGES(PKGS, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# -------------------------------------- +# Check that PKGS Vala bindings are installed and usable. +# +AC_DEFUN([VALA_CHECK_PACKAGES], +[ + unset vala_pkgs + unset vala_bindings + ac_save_ifs="$IFS"; unset IFS + for vala_pkg in $(echo "$1"); do + vala_pkgs="${vala_pkgs:+$vala_pkgs }--pkg $vala_pkg" + vala_bindings="${vala_bindings:+$vala_bindings }$vala_pkg" + done + IFS="$ac_save_ifs" + AC_MSG_CHECKING([for $vala_bindings vala bindings]) + _VALA_CHECK_COMPILE_WITH_ARGS([$vala_pkgs], + [vala_pkg_exists=yes], + [vala_pkg_exists=no]) + +AS_IF([test x${vala_pkg_exists} = xno],[ + ifelse([$3], , [AC_MSG_ERROR([]dnl +[Package requirements were not met: $1 + +$vala_error + +Consider adjusting the XDG_DATA_DIRS environment variable if you +installed bindings in a non-standard prefix. +])], + [AC_MSG_RESULT([no]) +$3])],[ + AC_MSG_RESULT([yes]) + ifelse([$2], , :, [$2])[]dnl +]) + +])# VALA_CHECK_PACKAGES + + +# Check for Vala bindings for a package, as well as the pkg-config +# CFLAGS and LIBS for the package. The arguments here work the +# same as those for PKG_CHECK_MODULES, which is called internally. +# As a result, the _CFLAGS, _LIBS, and _VALAFLAGS variables will +# all be declared, rather than only _VALAFLAGS. +# +# VALA_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# -------------------------------------------------------------- +AC_DEFUN([VALA_CHECK_MODULES], +[ + AC_REQUIRE([AM_PROG_VALAC])dnl + AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl + AC_REQUIRE([_VALA_CHECK_COMPILE_WITH_ARGS])dnl + AC_ARG_VAR([$1][_VALAFLAGS], [Vala compiler flags for $1])dnl + + VALA_MODULES="`echo $2 | sed -e 's/ [[=<>]]\+ [[0-9.]]\+//g'`" + for MODULE in $VALA_MODULES; do + $1[]_VALAFLAGS="$[]$1[]_VALAFLAGS --pkg $MODULE" + done + + PKG_CHECK_MODULES([$1], [$2], [$3], [$4]) + + pkg_failed=no + AC_MSG_CHECKING([for $1 vala modules]) + + _VALA_CHECK_COMPILE_WITH_ARGS([$1][_VALAFLAGS], + [pkg_failed=yes], + [pkg_failed=no]) + + if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_ERROR( + [Package requirements ($2) were not met.])dnl + ]) + else + AC_MSG_RESULT([yes]) + m4_default([$3], [:]) + fi[]dnl +]) + +# Check whether the Vala API Generator exists in `PATH'. If it is found, +# the variable VAPIGEN is set. Optionally a minimum release number of the +# generator can be requested. +# +# VALA_PROG_VAPIGEN([MINIMUM-VERSION]) +# ------------------------------------ +AC_DEFUN([VALA_PROG_VAPIGEN], +[AC_PATH_PROG([VAPIGEN], [vapigen], []) + AS_IF([test -z "$VAPIGEN"], + [AC_MSG_WARN([No Vala API Generator found. You will not be able to generate .vapi files.])], + [AS_IF([test -n "$1"], + [AC_MSG_CHECKING([$VAPIGEN is at least version $1]) + am__vapigen_version=`$VAPIGEN --version | sed 's/Vala API Generator *//'` + AS_VERSION_COMPARE([$1], ["$am__vapigen_version"], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([Vala API Generator $1 not found.])])])]) +]) diff --git a/m4/vapigen.m4 b/m4/vapigen.m4 new file mode 100644 index 00000000..2c435e74 --- /dev/null +++ b/m4/vapigen.m4 @@ -0,0 +1,101 @@ +dnl vapigen.m4 +dnl +dnl Copyright 2012 Evan Nemerson +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License, or (at your option) any later version. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# VAPIGEN_CHECK([VERSION], [API_VERSION], [FOUND_INTROSPECTION], [DEFAULT]) +# -------------------------------------- +# Check vapigen existence and version +# +# See http://live.gnome.org/Vala/UpstreamGuide for detailed documentation +AC_DEFUN([VAPIGEN_CHECK], +[ + AS_IF([test "x$3" != "xyes"], [ + m4_provide_if([GOBJECT_INTROSPECTION_CHECK], [], [ + m4_provide_if([GOBJECT_INTROSPECTION_REQUIRE], [], [ + AC_MSG_ERROR([[You must call GOBJECT_INTROSPECTION_CHECK or GOBJECT_INTROSPECTION_REQUIRE before using VAPIGEN_CHECK unless using the FOUND_INTROSPECTION argument is "yes"]]) + ]) + ]) + ]) + + AC_ARG_ENABLE([vala], + [AS_HELP_STRING([--enable-vala[=@<:@no/auto/yes@:>@]],[build Vala bindings @<:@default=]ifelse($4,,auto,$4)[@:>@])],,[ + AS_IF([test "x$4" = "x"], [ + enable_vala=auto + ], [ + enable_vala=$4 + ]) + ]) + + AS_CASE([$enable_vala], [no], [enable_vala=no], + [yes], [ + AS_IF([test "x$3" != "xyes" -a "x$found_introspection" != "xyes"], [ + AC_MSG_ERROR([Vala bindings require GObject Introspection]) + ]) + ], [auto], [ + AS_IF([test "x$3" != "xyes" -a "x$found_introspection" != "xyes"], [ + enable_vala=no + ]) + ], [ + AC_MSG_ERROR([Invalid argument passed to --enable-vala, should be one of @<:@no/auto/yes@:>@]) + ]) + + AS_IF([test "x$2" = "x"], [ + vapigen_pkg_name=vapigen + ], [ + vapigen_pkg_name=vapigen-$2 + ]) + AS_IF([test "x$1" = "x"], [ + vapigen_pkg="$vapigen_pkg_name" + ], [ + vapigen_pkg="$vapigen_pkg_name >= $1" + ]) + + PKG_PROG_PKG_CONFIG + + PKG_CHECK_EXISTS([$vapigen_pkg], [ + AS_IF([test "$enable_vala" = "auto"], [ + enable_vala=yes + ]) + ], [ + AS_CASE([$enable_vala], [yes], [ + AC_MSG_ERROR([$vapigen_pkg not found]) + ], [auto], [ + enable_vala=no + ]) + ]) + + AC_MSG_CHECKING([for vapigen]) + + AS_CASE([$enable_vala], + [yes], [ + VAPIGEN=`$PKG_CONFIG --variable=vapigen $vapigen_pkg_name` + VAPIGEN_MAKEFILE=`$PKG_CONFIG --variable=datadir $vapigen_pkg_name`/vala/Makefile.vapigen + AS_IF([test "x$2" = "x"], [ + VAPIGEN_VAPIDIR=`$PKG_CONFIG --variable=vapidir $vapigen_pkg_name` + ], [ + VAPIGEN_VAPIDIR=`$PKG_CONFIG --variable=vapidir_versioned $vapigen_pkg_name` + ]) + ]) + + AC_MSG_RESULT([$enable_vala]) + + AC_SUBST([VAPIGEN]) + AC_SUBST([VAPIGEN_VAPIDIR]) + AC_SUBST([VAPIGEN_MAKEFILE]) + + AM_CONDITIONAL(ENABLE_VAPIGEN, test "x$enable_vala" = "xyes") +]) diff --git a/m4/visibility.m4 b/m4/visibility.m4 new file mode 100644 index 00000000..6fff7459 --- /dev/null +++ b/m4/visibility.m4 @@ -0,0 +1,77 @@ +# visibility.m4 serial 5 (gettext-0.18.2) +dnl Copyright (C) 2005, 2008, 2010-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl Tests whether the compiler supports the command-line option +dnl -fvisibility=hidden and the function and variable attributes +dnl __attribute__((__visibility__("hidden"))) and +dnl __attribute__((__visibility__("default"))). +dnl Does *not* test for __visibility__("protected") - which has tricky +dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on +dnl Mac OS X. +dnl Does *not* test for __visibility__("internal") - which has processor +dnl dependent semantics. +dnl Does *not* test for #pragma GCC visibility push(hidden) - which is +dnl "really only recommended for legacy code". +dnl Set the variable CFLAG_VISIBILITY. +dnl Defines and sets the variable HAVE_VISIBILITY. + +AC_DEFUN([gl_VISIBILITY], +[ + AC_REQUIRE([AC_PROG_CC]) + CFLAG_VISIBILITY= + HAVE_VISIBILITY=0 + if test -n "$GCC"; then + dnl First, check whether -Werror can be added to the command line, or + dnl whether it leads to an error because of some other option that the + dnl user has put into $CC $CFLAGS $CPPFLAGS. + AC_MSG_CHECKING([whether the -Werror option is usable]) + AC_CACHE_VAL([gl_cv_cc_vis_werror], [ + gl_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Werror" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]], [[]])], + [gl_cv_cc_vis_werror=yes], + [gl_cv_cc_vis_werror=no]) + CFLAGS="$gl_save_CFLAGS"]) + AC_MSG_RESULT([$gl_cv_cc_vis_werror]) + dnl Now check whether visibility declarations are supported. + AC_MSG_CHECKING([for simple visibility declarations]) + AC_CACHE_VAL([gl_cv_cc_visibility], [ + gl_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fvisibility=hidden" + dnl We use the option -Werror and a function dummyfunc, because on some + dnl platforms (Cygwin 1.7) the use of -fvisibility triggers a warning + dnl "visibility attribute not supported in this configuration; ignored" + dnl at the first function definition in every compilation unit, and we + dnl don't want to use the option in this case. + if test $gl_cv_cc_vis_werror = yes; then + CFLAGS="$CFLAGS -Werror" + fi + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[extern __attribute__((__visibility__("hidden"))) int hiddenvar; + extern __attribute__((__visibility__("default"))) int exportedvar; + extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void); + extern __attribute__((__visibility__("default"))) int exportedfunc (void); + void dummyfunc (void) {} + ]], + [[]])], + [gl_cv_cc_visibility=yes], + [gl_cv_cc_visibility=no]) + CFLAGS="$gl_save_CFLAGS"]) + AC_MSG_RESULT([$gl_cv_cc_visibility]) + if test $gl_cv_cc_visibility = yes; then + CFLAG_VISIBILITY="-fvisibility=hidden" + HAVE_VISIBILITY=1 + fi + fi + AC_SUBST([CFLAG_VISIBILITY]) + AC_SUBST([HAVE_VISIBILITY]) + AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY], + [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.]) +]) diff --git a/m4/wchar_t.m4 b/m4/wchar_t.m4 new file mode 100644 index 00000000..dc964e67 --- /dev/null +++ b/m4/wchar_t.m4 @@ -0,0 +1,24 @@ +# wchar_t.m4 serial 4 (gettext-0.18.2) +dnl Copyright (C) 2002-2003, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Test whether has the 'wchar_t' type. +dnl Prerequisite: AC_PROG_CC + +AC_DEFUN([gt_TYPE_WCHAR_T], +[ + AC_CACHE_CHECK([for wchar_t], [gt_cv_c_wchar_t], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + wchar_t foo = (wchar_t)'\0';]], + [[]])], + [gt_cv_c_wchar_t=yes], + [gt_cv_c_wchar_t=no])]) + if test $gt_cv_c_wchar_t = yes; then + AC_DEFINE([HAVE_WCHAR_T], [1], [Define if you have the 'wchar_t' type.]) + fi +]) diff --git a/m4/wint_t.m4 b/m4/wint_t.m4 new file mode 100644 index 00000000..ca3fd449 --- /dev/null +++ b/m4/wint_t.m4 @@ -0,0 +1,32 @@ +# wint_t.m4 serial 5 (gettext-0.18.2) +dnl Copyright (C) 2003, 2007-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Test whether has the 'wint_t' type. +dnl Prerequisite: AC_PROG_CC + +AC_DEFUN([gt_TYPE_WINT_T], +[ + AC_CACHE_CHECK([for wint_t], [gt_cv_c_wint_t], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ +/* Tru64 with Desktop Toolkit C has a bug: must be included before + . + BSD/OS 4.0.1 has a bug: , and must be included + before . */ +#include +#include +#include +#include + wint_t foo = (wchar_t)'\0';]], + [[]])], + [gt_cv_c_wint_t=yes], + [gt_cv_c_wint_t=no])]) + if test $gt_cv_c_wint_t = yes; then + AC_DEFINE([HAVE_WINT_T], [1], [Define if you have the 'wint_t' type.]) + fi +]) diff --git a/m4/xsize.m4 b/m4/xsize.m4 new file mode 100644 index 00000000..98faf7de --- /dev/null +++ b/m4/xsize.m4 @@ -0,0 +1,12 @@ +# xsize.m4 serial 5 +dnl Copyright (C) 2003-2004, 2008-2015 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_XSIZE], +[ + dnl Prerequisites of lib/xsize.h. + AC_REQUIRE([gl_SIZE_MAX]) + AC_CHECK_HEADERS([stdint.h]) +]) diff --git a/m4/yelp.m4 b/m4/yelp.m4 new file mode 100644 index 00000000..5db847f6 --- /dev/null +++ b/m4/yelp.m4 @@ -0,0 +1,213 @@ +AC_DEFUN([YELP_HELP_INIT], +[ +AC_REQUIRE([AC_PROG_LN_S]) +m4_pattern_allow([AM_V_at]) +m4_pattern_allow([AM_V_GEN]) +m4_pattern_allow([AM_DEFAULT_VERBOSITY]) + +YELP_LC_MEDIA_LINKS=true +YELP_LC_DIST=true + +for yelpopt in [$1]; do + case $yelpopt in + lc-media-links) YELP_LC_MEDIA_LINKS=true ;; + no-lc-media-links) YELP_LC_MEDIA_LINKS= ;; + lc-dist) YELP_LC_DIST=true ;; + no-lc-dist) YELP_LC_DIST= ;; + *) AC_MSG_ERROR([Unrecognized [YELP_HELP_INIT] option $yelpopt"]) ;; + esac +done; +AC_SUBST([YELP_LC_MEDIA_LINKS]) +AC_SUBST([YELP_LC_DIST]) + +AC_ARG_WITH([help-dir], + AS_HELP_STRING([--with-help-dir=DIR], + [path where help files are installed]),, + [with_help_dir='${datadir}/help']) +HELP_DIR="$with_help_dir" +AC_SUBST(HELP_DIR) + +AC_ARG_VAR([ITSTOOL], [Path to the `itstool` command]) +AC_CHECK_PROG([ITSTOOL], [itstool], [itstool]) +if test x"$ITSTOOL" = x; then + AC_MSG_ERROR([itstool not found]) +fi + +AC_ARG_VAR([XMLLINT], [Path to the `xmllint` command]) +AC_CHECK_PROG([XMLLINT], [xmllint], [xmllint]) +if test x"$XMLLINT" = x; then + AC_MSG_ERROR([xmllint not found]) +fi + +YELP_HELP_RULES=' +HELP_ID ?= +HELP_POT ?= +HELP_FILES ?= +HELP_EXTRA ?= +HELP_MEDIA ?= +HELP_LINGUAS ?= + +_HELP_LINGUAS = $(if $(filter environment,$(origin LINGUAS)),$(filter $(LINGUAS),$(HELP_LINGUAS)),$(HELP_LINGUAS)) +_HELP_POTFILE = $(if $(HELP_POT),$(HELP_POT),$(if $(HELP_ID),$(HELP_ID).pot)) +_HELP_POFILES = $(if $(HELP_ID),$(foreach lc,$(_HELP_LINGUAS),$(lc)/$(lc).po)) +_HELP_MOFILES = $(patsubst %.po,%.mo,$(_HELP_POFILES)) +_HELP_C_FILES = $(foreach f,$(HELP_FILES),C/$(f)) +_HELP_C_EXTRA = $(foreach f,$(HELP_EXTRA),C/$(f)) +_HELP_C_MEDIA = $(foreach f,$(HELP_MEDIA),C/$(f)) +_HELP_LC_FILES = $(foreach lc,$(_HELP_LINGUAS),$(foreach f,$(HELP_FILES),$(lc)/$(f))) +_HELP_LC_STAMPS = $(foreach lc,$(_HELP_LINGUAS),$(lc)/$(lc).stamp) + +_HELP_DEFAULT_V = $(if $(AM_DEFAULT_VERBOSITY),$(AM_DEFAULT_VERBOSITY),1) +_HELP_V = $(if $(V),$(V),$(_HELP_DEFAULT_V)) +_HELP_LC_VERBOSE = $(_HELP_LC_VERBOSE_$(_HELP_V)) +_HELP_LC_VERBOSE_ = $(_HELP_LC_VERBOSE_$(_HELP_DEFAULT_V)) +_HELP_LC_VERBOSE_0 = @echo " GEN "$(dir [$]@); + +all: $(_HELP_C_FILES) $(_HELP_C_EXTRA) $(_HELP_C_MEDIA) $(_HELP_LC_FILES) $(_HELP_POFILES) + +.PHONY: pot +pot: $(_HELP_POTFILE) +$(_HELP_POTFILE): $(_HELP_C_FILES) $(_HELP_C_EXTRA) $(_HELP_C_MEDIA) + $(AM_V_GEN)if test -d "C"; then d=; else d="$(srcdir)/"; fi; \ + $(ITSTOOL) -o "[$]@" $(foreach f,$(_HELP_C_FILES),"$${d}$(f)") + +.PHONY: repo +repo: $(_HELP_POTFILE) + $(AM_V_at)for po in $(_HELP_POFILES); do \ + if test "x[$](_HELP_V)" = "x0"; then echo " GEN $${po}"; fi; \ + msgmerge -q -o "$${po}" "$${po}" "$(_HELP_POTFILE)"; \ + done + +$(_HELP_POFILES): + $(AM_V_at)if ! test -d "$(dir [$]@)"; then mkdir "$(dir [$]@)"; fi + $(AM_V_at)if test ! -f "[$]@" -a -f "$(srcdir)/[$]@"; then cp "$(srcdir)/[$]@" "[$]@"; fi + $(AM_V_GEN)if ! test -f "[$]@"; then \ + (cd "$(dir [$]@)" && \ + $(ITSTOOL) -o "$(notdir [$]@).tmp" $(_HELP_C_FILES) && \ + mv "$(notdir [$]@).tmp" "$(notdir [$]@)"); \ + else \ + (cd "$(dir [$]@)" && \ + $(ITSTOOL) -o "$(notdir [$]@).tmp" $(_HELP_C_FILES) && \ + msgmerge -o "$(notdir [$]@)" "$(notdir [$]@)" "$(notdir [$]@).tmp" && \ + rm "$(notdir [$]@).tmp"); \ + fi + +$(_HELP_MOFILES): %.mo: %.po + $(AM_V_at)if ! test -d "$(dir [$]@)"; then mkdir "$(dir [$]@)"; fi + $(AM_V_GEN)msgfmt -o "[$]@" "$<" + +$(_HELP_LC_FILES): $(_HELP_LINGUAS) +$(_HELP_LINGUAS): $(_HELP_LC_STAMPS) +$(_HELP_LC_STAMPS): %.stamp: %.mo +$(_HELP_LC_STAMPS): $(_HELP_C_FILES) $(_HELP_C_EXTRA) + $(AM_V_at)if ! test -d "$(dir [$]@)"; then mkdir "$(dir [$]@)"; fi + $(_HELP_LC_VERBOSE)if test -d "C"; then d="../"; else d="$(abs_srcdir)/"; fi; \ + mo="$(dir [$]@)$(patsubst %/$(notdir [$]@),%,[$]@).mo"; \ + if test -f "$${mo}"; then mo="../$${mo}"; else mo="$(abs_srcdir)/$${mo}"; fi; \ + (cd "$(dir [$]@)" && $(ITSTOOL) -m "$${mo}" $(foreach f,$(_HELP_C_FILES),$${d}/$(f))) && \ + touch "[$]@" + +.PHONY: clean-help +mostlyclean-am: $(if $(HELP_ID),clean-help) +clean-help: + rm -f $(_HELP_LC_FILES) $(_HELP_LC_STAMPS) $(_HELP_MOFILES) + +EXTRA_DIST ?= +EXTRA_DIST += $(_HELP_C_EXTRA) $(_HELP_C_MEDIA) +EXTRA_DIST += $(if $(YELP_LC_DIST),$(foreach lc,$(HELP_LINGUAS),$(lc)/$(lc).stamp)) +EXTRA_DIST += $(foreach lc,$(HELP_LINGUAS),$(lc)/$(lc).po) +EXTRA_DIST += $(foreach f,$(HELP_MEDIA),$(foreach lc,$(HELP_LINGUAS),$(wildcard $(lc)/$(f)))) + +distdir: distdir-help-files +distdir-help-files: $(_HELP_LC_FILES) + @for lc in C $(if $(YELP_LC_DIST),$(HELP_LINGUAS)) ; do \ + $(MKDIR_P) "$(distdir)/$$lc"; \ + for file in $(HELP_FILES); do \ + if test -f "$$lc/$$file"; then d=./; else d=$(srcdir)/; fi; \ + cp -p "$$d$$lc/$$file" "$(distdir)/$$lc/" || exit 1; \ + done; \ + done; \ + +.PHONY: check-help +check: check-help +check-help: + for lc in C $(_HELP_LINGUAS); do \ + if test -d "$$lc"; \ + then d=; \ + xmlpath="$$lc"; \ + else \ + d="$(srcdir)/"; \ + xmlpath="$$lc:$(srcdir)/$$lc"; \ + fi; \ + for page in $(HELP_FILES); do \ + echo "$(XMLLINT) --noout --noent --path $$xmlpath --xinclude $$d$$lc/$$page"; \ + $(XMLLINT) --noout --noent --path "$$xmlpath" --xinclude "$$d$$lc/$$page"; \ + done; \ + done + + +.PHONY: install-help +install-data-am: $(if $(HELP_ID),install-help) +install-help: $(_HELP_LC_FILES) + @for lc in C $(_HELP_LINGUAS); do \ + $(mkinstalldirs) "$(DESTDIR)$(HELP_DIR)/$$lc/$(HELP_ID)" || exit 1; \ + done + @for lc in C $(_HELP_LINGUAS); do for f in $(HELP_FILES); do \ + if test -f "$$lc/$$f"; then d=; else d="$(srcdir)/"; fi; \ + helpdir="$(DESTDIR)$(HELP_DIR)/$$lc/$(HELP_ID)/"; \ + if ! test -d "$$helpdir"; then $(mkinstalldirs) "$$helpdir"; fi; \ + echo "$(INSTALL_DATA) $$d$$lc/$$f $$helpdir`basename $$f`"; \ + $(INSTALL_DATA) "$$d$$lc/$$f" "$$helpdir`basename $$f`" || exit 1; \ + done; done + @for f in $(_HELP_C_EXTRA); do \ + lc=`dirname "$$f"`; lc=`basename "$$lc"`; \ + if test -f "$$f"; then d=; else d="$(srcdir)/"; fi; \ + helpdir="$(DESTDIR)$(HELP_DIR)/$$lc/$(HELP_ID)/"; \ + if ! test -d "$$helpdir"; then $(mkinstalldirs) "$$helpdir"; fi; \ + echo "$(INSTALL_DATA) $$d$$f $$helpdir`basename $$f`"; \ + $(INSTALL_DATA) "$$d$$f" "$$helpdir`basename $$f`" || exit 1; \ + done + @for f in $(HELP_MEDIA); do \ + for lc in C $(_HELP_LINGUAS); do \ + if test -f "$$lc$$f"; then d=; else d="$(srcdir)/"; fi; \ + helpdir="$(DESTDIR)$(HELP_DIR)/$$lc/$(HELP_ID)/"; \ + mdir=`dirname "$$f"`; \ + if test "x$mdir" = "x."; then mdir=""; fi; \ + if ! test -d "$$helpdir$$mdir"; then $(mkinstalldirs) "$$helpdir$$mdir"; fi; \ + if test -f "$$d$$lc/$$f"; then \ + echo "$(INSTALL_DATA) $$d$$lc/$$f $$helpdir$$f"; \ + $(INSTALL_DATA) "$$d$$lc/$$f" "$$helpdir$$f" || exit 1; \ + elif test "x$$lc" != "xC"; then \ + if test "x$(YELP_LC_MEDIA_LINKS)" != "x"; then \ + echo "$(LN_S) -f $(HELP_DIR)/C/$(HELP_ID)/$$f $$helpdir$$f"; \ + $(LN_S) -f "$(HELP_DIR)/C/$(HELP_ID)/$$f" "$$helpdir$$f" || exit 1; \ + fi; \ + fi; \ + done; \ + done + +.PHONY: uninstall-help +uninstall-am: $(if $(HELP_ID),uninstall-help) +uninstall-help: + for lc in C $(_HELP_LINGUAS); do for f in $(HELP_FILES); do \ + helpdir="$(DESTDIR)$(HELP_DIR)/$$lc/$(HELP_ID)/"; \ + echo "rm -f $$helpdir`basename $$f`"; \ + rm -f "$$helpdir`basename $$f`"; \ + done; done + @for f in $(_HELP_C_EXTRA); do \ + lc=`dirname "$$f"`; lc=`basename "$$lc"`; \ + helpdir="$(DESTDIR)$(HELP_DIR)/$$lc/$(HELP_ID)/"; \ + echo "rm -f $$helpdir`basename $$f`"; \ + rm -f "$$helpdir`basename $$f`"; \ + done + @for f in $(HELP_MEDIA); do \ + for lc in C $(_HELP_LINGUAS); do \ + helpdir="$(DESTDIR)$(HELP_DIR)/$$lc/$(HELP_ID)/"; \ + echo "rm -f $$helpdir$$f"; \ + rm -f "$$helpdir$$f"; \ + done; \ + done; +' +AC_SUBST([YELP_HELP_RULES]) +m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([YELP_HELP_RULES])]) +]) diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..9973c6fb --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,38 @@ +bin_PROGRAMS = sysprof + +sysprof_CFLAGS = \ + $(SYSPROF_CFLAGS) \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/lib + +sysprof_SOURCES = \ + sysprof.c \ + sp-application.c \ + sp-application.h \ + sp-credits.h \ + sp-window.c \ + sp-window.h \ + sp-window-settings.c \ + sp-window-settings.h \ + $(NULL) + +nodist_sysprof_SOURCES = \ + sp-resources.c \ + sp-resources.h + +sysprof_LDADD = \ + $(SYSPROF_LIBS) \ + $(top_builddir)/lib/libsysprof-@API_VERSION@.la + +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +BUILT_SOURCES = + +glib_resources_xml = resources/sysprof.gresource.xml +glib_resources_c = sp-resources.c +glib_resources_h = sp-resources.h +glib_resources_namespace = sysprof +include $(top_srcdir)/gresources.mk + +-include $(top_srcdir)/git.mk diff --git a/src/resources/gtk/menus.ui b/src/resources/gtk/menus.ui new file mode 100644 index 00000000..94b99a0c --- /dev/null +++ b/src/resources/gtk/menus.ui @@ -0,0 +1,51 @@ + + + +
+ + _New Window + app.new-window + +
+
+ + _Open Capture + app.open-capture + +
+
+ + About + app.about + + + Help + app.help + + + _Quit + app.quit + +
+
+ +
+ + Open + win.open-capture + +
+
+ + Save As + win.save-capture + +
+
+ + Close + win.close-capture + +
+
+
diff --git a/src/resources/sysprof.gresource.xml b/src/resources/sysprof.gresource.xml new file mode 100644 index 00000000..b6735f39 --- /dev/null +++ b/src/resources/sysprof.gresource.xml @@ -0,0 +1,21 @@ + + + + + gtk/menus.ui + + + ../../data/icons/16x16/apps/sysprof.png + ../../data/icons/24x24/apps/sysprof.png + ../../data/icons/32x32/apps/sysprof.png + ../../data/icons/48x48/apps/sysprof.png + ../../data/icons/256x256/apps/sysprof.png + ../../data/icons/scalable/apps/sysprof-symbolic.svg + + + theme/shared.css + + + ui/sp-window.ui + + diff --git a/src/resources/theme/shared.css b/src/resources/theme/shared.css new file mode 100644 index 00000000..3d4da45b --- /dev/null +++ b/src/resources/theme/shared.css @@ -0,0 +1,12 @@ +popover list row { + padding: 6px 10px 6px 10px; + border-bottom: 1px solid alpha(@borders, 0.2); +} + +popover list row:last-child { + border-bottom: none; +} + +popover scrolledwindow { + border-top: 1px solid alpha(@borders, 0.75); +} diff --git a/src/resources/ui/sp-window.ui b/src/resources/ui/sp-window.ui new file mode 100644 index 00000000..298c4641 --- /dev/null +++ b/src/resources/ui/sp-window.ui @@ -0,0 +1,174 @@ + + + + diff --git a/src/sp-application.c b/src/sp-application.c new file mode 100644 index 00000000..776f57b9 --- /dev/null +++ b/src/sp-application.c @@ -0,0 +1,271 @@ +/* sp-application.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "sp-application.h" +#include "sp-credits.h" +#include "sp-window.h" + +struct _SpApplication +{ + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE (SpApplication, sp_application, GTK_TYPE_APPLICATION) + +static void +sp_application_activate (GApplication *app) +{ + SpWindow *window; + GList *windows; + + g_assert (GTK_IS_APPLICATION (app)); + + windows = gtk_application_get_windows (GTK_APPLICATION (app)); + + for (; windows != NULL; windows = windows->next) + { + if (SP_IS_WINDOW (windows->data)) + { + gtk_window_present (windows->data); + return; + } + } + + window = g_object_new (SP_TYPE_WINDOW, + "application", app, + NULL); + + gtk_window_present (GTK_WINDOW (window)); +} + +static void +sp_application_open (GApplication *app, + GFile **files, + gint n_files, + const gchar *hint) +{ + guint opened = 0; + gint i; + + g_assert (SP_IS_APPLICATION (app)); + g_assert (files != NULL || n_files == 0); + + for (i = 0; i < n_files; i++) + { + SpWindow *window; + + window = g_object_new (SP_TYPE_WINDOW, + "application", app, + NULL); + sp_window_open (window, files [i]); + gtk_window_present (GTK_WINDOW (window)); + opened++; + } + + if (opened == 0) + sp_application_activate (app); +} + +static void +sp_application_startup (GApplication *application) +{ + GtkCssProvider *provider; + + g_assert (SP_IS_APPLICATION (application)); + + G_APPLICATION_CLASS (sp_application_parent_class)->startup (application); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, "/org/gnome/sysprof/theme/shared.css"); + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_clear_object (&provider); +} + +static void +sp_application_class_init (SpApplicationClass *klass) +{ + GApplicationClass *app_class = G_APPLICATION_CLASS (klass); + + app_class->open = sp_application_open; + app_class->startup = sp_application_startup; + app_class->activate = sp_application_activate; +} + +static void +sysprof_quit (GSimpleAction *action, + GVariant *variant, + gpointer user_data) +{ + GApplication *app = user_data; + + g_assert (G_IS_APPLICATION (app)); + g_assert (G_IS_SIMPLE_ACTION (action)); + g_assert (variant == NULL); + + g_application_quit (app); +} + +static void +sysprof_about (GSimpleAction *action, + GVariant *variant, + gpointer user_data) +{ + GtkApplication *app = user_data; + GtkWindow *best_toplevel = NULL; + GtkWindow *dialog; + GList *windows; + + g_assert (G_IS_APPLICATION (app)); + g_assert (G_IS_SIMPLE_ACTION (action)); + g_assert (variant == NULL); + + windows = gtk_application_get_windows (app); + + for (; windows != NULL; windows = windows->next) + { + if (SP_IS_WINDOW (windows->data)) + { + best_toplevel = windows->data; + break; + } + } + + dialog = g_object_new (GTK_TYPE_ABOUT_DIALOG, + "application", app, + "authors", sysprof_authors, + "artists", sysprof_artists, + "comments", _("A system profiler"), + "copyright", "Copyright © 2004-2009 Søren Sandmann Pedersen\n" + "Copyright © 2016 Christian Hergert", + "transient-for", best_toplevel, + "translator-credits", _("translator-credits"), + "license-type", GTK_LICENSE_GPL_3_0, + "logo-icon-name", "sysprof", + "program-name", _("Sysprof"), + "version", PACKAGE_VERSION, + "website", "https://wiki.gnome.org/Apps/Sysprof", + "website-label", _("Learn more about Sysprof"), + NULL); + + gtk_window_present (dialog); +} + +static void +sysprof_help (GSimpleAction *action, + GVariant *param, + gpointer user_data) +{ + gtk_show_uri (gdk_screen_get_default (), + "help:sysprof", + gtk_get_current_event_time (), + NULL); +} + +static void +sysprof_new_window (GSimpleAction *action, + GVariant *variant, + gpointer user_data) +{ + SpApplication *self = user_data; + SpWindow *window; + + g_assert (SP_IS_APPLICATION (self)); + g_assert (G_IS_SIMPLE_ACTION (action)); + g_assert (variant == NULL); + + window = g_object_new (SP_TYPE_WINDOW, + "application", self, + NULL); + gtk_window_present (GTK_WINDOW (window)); +} + +static void +sysprof_open_capture (GSimpleAction *action, + GVariant *variant, + gpointer user_data) +{ + GtkApplication *app = user_data; + GtkWidget *window; + GList *list; + + g_assert (G_IS_APPLICATION (app)); + g_assert (G_IS_SIMPLE_ACTION (action)); + g_assert (variant == NULL); + + list = gtk_application_get_windows (app); + + for (; list != NULL; list = list->next) + { + window = list->data; + + if (SP_IS_WINDOW (window)) + { + SpWindowState state; + + state = sp_window_get_state (SP_WINDOW (window)); + + if (state == SP_WINDOW_STATE_EMPTY) + { + sp_window_open_from_dialog (SP_WINDOW (window)); + return; + } + } + } + + window = g_object_new (SP_TYPE_WINDOW, + "application", app, + NULL); + + gtk_window_present (GTK_WINDOW (window)); + + sp_window_open_from_dialog (SP_WINDOW (window)); +} + +static void +sp_application_init (SpApplication *self) +{ + static const GActionEntry actions[] = { + { "about", sysprof_about }, + { "new-window", sysprof_new_window }, + { "open-capture", sysprof_open_capture }, + { "help", sysprof_help }, + { "quit", sysprof_quit }, + }; + + g_action_map_add_action_entries (G_ACTION_MAP (self), actions, G_N_ELEMENTS (actions), self); + + g_application_set_default (G_APPLICATION (self)); +} + +SpApplication * +sp_application_new (void) +{ + return g_object_new (SP_TYPE_APPLICATION, + "application-id", "org.gnome.Sysprof2", + "resource-base-path", "/org/gnome/sysprof", + "flags", G_APPLICATION_HANDLES_OPEN, + NULL); +} diff --git a/src/sp-application.h b/src/sp-application.h new file mode 100644 index 00000000..ab5af6f6 --- /dev/null +++ b/src/sp-application.h @@ -0,0 +1,34 @@ +/* sp-application.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_APPLICATION_H +#define SP_APPLICATION_H + +#include + +G_BEGIN_DECLS + +#define SP_TYPE_APPLICATION (sp_application_get_type()) + +G_DECLARE_FINAL_TYPE (SpApplication, sp_application, SP, APPLICATION, GtkApplication) + +SpApplication *sp_application_new (void); + +G_END_DECLS + +#endif /* SP_APPLICATION_H */ diff --git a/src/sp-credits.h b/src/sp-credits.h new file mode 100644 index 00000000..acaf8942 --- /dev/null +++ b/src/sp-credits.h @@ -0,0 +1,38 @@ +#ifndef SP_CREDITS_H +#define SP_CREDITS_H + +#include + +G_BEGIN_DECLS + +static gchar *sysprof_authors[] = { + "Bastien Nocera", + "Behdad Esfahbod", + "Christian Hergert", + "Damien Lespiau", + "Geoffrey Keating", + "Javier Jardón", + "Jeffrey Stedfast", + "Kristian Høgsberg", + "Kristian Rietvel", + "M Joonas Pihlaja", + "Matthias Clasen", + "Michel Dänzer", + "Nix", + "Owen Taylor", + "Paolo Borelli", + "Pascal Terjan", + "Pauli Nieminen", + "Robert Love", + "Søren Sandmann Pedersen", + NULL +}; + +static gchar *sysprof_artists[] = { + "Jakub Steiner", + NULL +}; + +G_END_DECLS + +#endif /* SP_CREDITS_H */ diff --git a/src/sp-window-settings.c b/src/sp-window-settings.c new file mode 100644 index 00000000..7d60e940 --- /dev/null +++ b/src/sp-window-settings.c @@ -0,0 +1,175 @@ +/* sp-window-settings.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 "sp-window.h" +#include "sp-window-settings.h" + +#define WINDOW_MIN_WIDTH 1200 +#define WINDOW_MIN_HEIGHT 700 +#define SAVE_TIMEOUT_SECS 1 + +static GSettings *settings; + +static gboolean +sp_window_settings__window_save_settings_cb (gpointer data) +{ + GtkWindow *window = data; + GdkRectangle geom; + gboolean maximized; + + g_assert (GTK_IS_WINDOW (window)); + g_assert (G_IS_SETTINGS (settings)); + + g_object_set_data (G_OBJECT (window), "SETTINGS_HANDLER_ID", NULL); + + gtk_window_get_size (window, &geom.width, &geom.height); + gtk_window_get_position (window, &geom.x, &geom.y); + maximized = gtk_window_is_maximized (window); + + g_settings_set (settings, "window-size", "(ii)", geom.width, geom.height); + g_settings_set (settings, "window-position", "(ii)", geom.x, geom.y); + g_settings_set_boolean (settings, "window-maximized", maximized); + + return G_SOURCE_REMOVE; +} + +static gboolean +sp_window_settings__window_configure_event (GtkWindow *window, + GdkEventConfigure *event) +{ + guint handler; + + g_assert (GTK_IS_WINDOW (window)); + g_assert (event != NULL); + g_assert (G_IS_SETTINGS (settings)); + + handler = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "SETTINGS_HANDLER_ID")); + + if (handler == 0) + { + handler = g_timeout_add_seconds (SAVE_TIMEOUT_SECS, + sp_window_settings__window_save_settings_cb, + window); + g_object_set_data (G_OBJECT (window), "SETTINGS_HANDLER_ID", GINT_TO_POINTER (handler)); + } + + return GDK_EVENT_PROPAGATE; +} + +static void +sp_window_settings__window_realize (GtkWindow *window) +{ + GtkApplication *app; + GdkRectangle geom = { 0 }; + gboolean maximized = FALSE; + GList *list; + guint count = 0; + + g_assert (GTK_IS_WINDOW (window)); + g_assert (G_IS_SETTINGS (settings)); + + g_settings_get (settings, "window-position", "(ii)", &geom.x, &geom.y); + g_settings_get (settings, "window-size", "(ii)", &geom.width, &geom.height); + g_settings_get (settings, "window-maximized", "b", &maximized); + + geom.width = MAX (geom.width, WINDOW_MIN_WIDTH); + geom.height = MAX (geom.height, WINDOW_MIN_HEIGHT); + gtk_window_set_default_size (window, geom.width, geom.height); + + /* + * If there are other windows currently visible other than this one, + * then ignore positioning and let the window manager decide. + */ + count = 0; + app = GTK_APPLICATION (g_application_get_default ()); + list = gtk_application_get_windows (app); + for (; list != NULL; list = list->next) + { + GtkWindow *ele = list->data; + + if (SP_IS_WINDOW (ele) && (ele != window) && + gtk_widget_get_visible (GTK_WIDGET (window))) + count++; + } + + if (count == 0) + gtk_window_move (window, geom.x, geom.y); + + if (maximized) + gtk_window_maximize (window); +} + +static void +sp_window_settings__window_destroy (GtkWindow *window) +{ + guint handler; + + g_assert (GTK_IS_WINDOW (window)); + g_assert (G_IS_SETTINGS (settings)); + + handler = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "SETTINGS_HANDLER_ID")); + + if (handler != 0) + { + g_source_remove (handler); + g_object_set_data (G_OBJECT (window), "SETTINGS_HANDLER_ID", NULL); + } + + g_signal_handlers_disconnect_by_func (window, + G_CALLBACK (sp_window_settings__window_configure_event), + NULL); + + g_signal_handlers_disconnect_by_func (window, + G_CALLBACK (sp_window_settings__window_destroy), + NULL); + + g_signal_handlers_disconnect_by_func (window, + G_CALLBACK (sp_window_settings__window_realize), + NULL); + + g_object_unref (settings); +} + +void +sp_window_settings_register (GtkWindow *window) +{ + if (settings == NULL) + { + settings = g_settings_new ("org.gnome.sysprof2"); + g_object_add_weak_pointer (G_OBJECT (settings), (gpointer *)&settings); + } + else + { + g_object_ref (settings); + } + + g_signal_connect (window, + "configure-event", + G_CALLBACK (sp_window_settings__window_configure_event), + NULL); + + g_signal_connect (window, + "destroy", + G_CALLBACK (sp_window_settings__window_destroy), + NULL); + + g_signal_connect (window, + "realize", + G_CALLBACK (sp_window_settings__window_realize), + NULL); +} diff --git a/src/sp-window-settings.h b/src/sp-window-settings.h new file mode 100644 index 00000000..7173e970 --- /dev/null +++ b/src/sp-window-settings.h @@ -0,0 +1,30 @@ +/* sp-window-settings.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_WINDOW_SETTINGS_H +#define SP_WINDOW_SETTINGS_H + +#include + +G_BEGIN_DECLS + +void sp_window_settings_register (GtkWindow *window); + +G_END_DECLS + +#endif /* SP_WINDOW_SETTINGS_H */ diff --git a/src/sp-window.c b/src/sp-window.c new file mode 100644 index 00000000..6ceb9e68 --- /dev/null +++ b/src/sp-window.c @@ -0,0 +1,853 @@ +/* sp-window.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 +#include + +#include "sp-application.h" +#include "sp-window.h" +#include "sp-window-settings.h" + +struct _SpWindow +{ + GtkApplicationWindow parent_instance; + + SpWindowState state; + + SpProfiler *profiler; + SpCaptureReader *reader; + + /* Gtk widget template children */ + SpCallgraphView *callgraph_view; + SpEmptyStateView *empty_view; + GtkMenuButton *gear_menu_button; + GtkInfoBar *info_bar; + GtkLabel *info_bar_label; + GtkRevealer *info_bar_revealer; + SpProfilerMenuButton *profiler_menu_button; + SpRecordingStateView *recording_view; + GtkButton *record_button; + GtkLabel *subtitle; + GtkLabel *stat_label; + GtkLabel *title; + GtkStack *view_stack; + + guint stats_handler; +}; + +G_DEFINE_TYPE (SpWindow, sp_window, GTK_TYPE_APPLICATION_WINDOW) + +static void sp_window_set_state (SpWindow *self, + SpWindowState state); + +enum { + START_RECORDING, + STOP_RECORDING, + N_SIGNALS +}; + +static guint signals [N_SIGNALS]; + +static void sp_window_set_profiler (SpWindow *self, + SpProfiler *profiler); + +static void +sp_window_notify_user (SpWindow *self, + GtkMessageType message_type, + const gchar *format, + ...) +{ + g_autofree gchar *str = NULL; + va_list args; + + g_assert (SP_IS_WINDOW (self)); + g_assert (format != NULL); + + va_start (args, format); + str = g_strdup_vprintf (format, args); + va_end (args); + + gtk_info_bar_set_message_type (self->info_bar, message_type); + gtk_label_set_label (self->info_bar_label, str); + gtk_revealer_set_reveal_child (self->info_bar_revealer, TRUE); +} + +static void +sp_window_action_set (SpWindow *self, + const gchar *action_name, + const gchar *first_property, + ...) +{ + gpointer action; + va_list args; + + g_assert (SP_IS_WINDOW (self)); + g_assert (action_name != NULL); + + action = g_action_map_lookup_action (G_ACTION_MAP (self), action_name); + + if (action == NULL) + { + g_warning ("Failed to locate action \"%s\"", action_name); + return; + } + + va_start (args, first_property); + g_object_set_valist (action, first_property, args); + va_end (args); +} + +static gboolean +sp_window_update_stats (gpointer data) +{ + SpWindow *self = data; + + g_assert (SP_IS_WINDOW (self)); + + if (self->profiler != NULL) + { + SpCaptureWriter *writer; + + if (NULL != (writer = sp_profiler_get_writer (self->profiler))) + { + g_autofree gchar *str = NULL; + SpCaptureStat stbuf; + guint count; + + sp_capture_writer_stat (writer, &stbuf); + + count = stbuf.frame_count[SP_CAPTURE_FRAME_SAMPLE]; + str = g_strdup_printf (_("Samples: %u"), count); + gtk_label_set_label (self->stat_label, str); + } + } + + return G_SOURCE_CONTINUE; +} + + +static void +sp_window_update_subtitle (SpWindow *self) +{ + g_autofree gchar *relative = NULL; + const gchar *filename; + const gchar *date; + GTimeVal tv; + + g_assert (SP_IS_WINDOW (self)); + g_assert (self->reader != NULL); + + if (NULL != (filename = sp_capture_reader_get_filename (self->reader))) + { + g_autoptr(GFile) home = NULL; + g_autoptr(GFile) file = NULL; + + file = g_file_new_for_path (filename); + home = g_file_new_for_path (g_get_home_dir ()); + + if (g_file_has_prefix (file, home)) + filename = relative = g_file_get_relative_path (home, file); + } + + if (filename == NULL) + filename = _("[Memory Capture]"); + + date = sp_capture_reader_get_time (self->reader); + + if (g_time_val_from_iso8601 (date, &tv)) + { + g_autoptr(GDateTime) dt = NULL; + g_autofree gchar *str = NULL; + g_autofree gchar *label = NULL; + + dt = g_date_time_new_from_timeval_local (&tv); + str = g_date_time_format (dt, "%x %X"); + + label = g_strdup_printf (_("%s - %s"), filename, str); + + gtk_label_set_label (self->subtitle, label); + } + else + gtk_label_set_label (self->subtitle, filename); +} + +static void +sp_window_build_profile_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SpProfile *profile = (SpProfile *)object; + g_autoptr(SpWindow) self = user_data; + g_autoptr(GError) error = NULL; + + g_assert (SP_IS_CALLGRAPH_PROFILE (profile)); + g_assert (SP_IS_WINDOW (self)); + g_assert (G_IS_ASYNC_RESULT (result)); + + if (!sp_profile_generate_finish (profile, result, &error)) + { + sp_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", error->message); + sp_window_set_state (self, SP_WINDOW_STATE_EMPTY); + return; + } + + sp_callgraph_view_set_profile (self->callgraph_view, SP_CALLGRAPH_PROFILE (profile)); + sp_window_set_state (self, SP_WINDOW_STATE_BROWSING); +} + +static void +sp_window_build_profile (SpWindow *self) +{ + g_autoptr(SpProfile) profile = NULL; + + g_assert (SP_IS_WINDOW (self)); + g_assert (self->reader != NULL); + + profile = sp_callgraph_profile_new (); + sp_profile_set_reader (profile, self->reader); + sp_profile_generate (profile, + NULL, + sp_window_build_profile_cb, + g_object_ref (self)); +} + +static void +add_class (gpointer widget, + const gchar *name) +{ + g_assert (GTK_IS_WIDGET (widget)); + + gtk_style_context_add_class (gtk_widget_get_style_context (widget), name); +} + +static void +remove_class (gpointer widget, + const gchar *name) +{ + g_assert (GTK_IS_WIDGET (widget)); + + gtk_style_context_remove_class (gtk_widget_get_style_context (widget), name); +} + +static void +sp_window_set_state (SpWindow *self, + SpWindowState state) +{ + g_autoptr(SpProfiler) profiler = NULL; + + g_assert (SP_IS_WINDOW (self)); + + if (self->state == state) + return; + + self->state = state; + + switch (state) + { + case SP_WINDOW_STATE_EMPTY: + case SP_WINDOW_STATE_FAILED: + profiler = sp_profiler_new (); + + gtk_button_set_label (self->record_button, _("Record")); + gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE); + add_class (self->record_button, "suggsted-action"); + remove_class (self->record_button, "destructive-action"); + if (state == SP_WINDOW_STATE_FAILED) + gtk_stack_set_visible_child_name (self->view_stack, "failed"); + else + gtk_stack_set_visible_child_name (self->view_stack, "empty"); + gtk_label_set_label (self->subtitle, _("Not running")); + sp_callgraph_view_set_profile (self->callgraph_view, NULL); + gtk_widget_set_visible (GTK_WIDGET (self->stat_label), FALSE); + g_clear_pointer (&self->reader, sp_capture_reader_unref); + sp_window_set_profiler (self, profiler); + sp_window_action_set (self, "close-capture", "enabled", FALSE, NULL); + sp_window_action_set (self, "save-capture", "enabled", FALSE, NULL); + break; + + case SP_WINDOW_STATE_RECORDING: + gtk_button_set_label (self->record_button, _("Stop")); + gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE); + remove_class (self->record_button, "suggsted-action"); + add_class (self->record_button, "destructive-action"); + gtk_stack_set_visible_child_name (self->view_stack, "recording"); + gtk_label_set_label (self->subtitle, _("Recording…")); + gtk_widget_set_visible (GTK_WIDGET (self->stat_label), TRUE); + g_clear_pointer (&self->reader, sp_capture_reader_unref); + sp_callgraph_view_set_profile (self->callgraph_view, NULL); + sp_window_action_set (self, "close-capture", "enabled", FALSE, NULL); + sp_window_action_set (self, "save-capture", "enabled", FALSE, NULL); + break; + + case SP_WINDOW_STATE_PROCESSING: + gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), FALSE); + gtk_label_set_label (self->subtitle, _("Building profile…")); + sp_window_action_set (self, "close-capture", "enabled", FALSE, NULL); + sp_window_action_set (self, "save-capture", "enabled", FALSE, NULL); + sp_window_build_profile (self); + break; + + case SP_WINDOW_STATE_BROWSING: + gtk_button_set_label (self->record_button, _("Record")); + gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE); + add_class (self->record_button, "suggsted-action"); + remove_class (self->record_button, "destructive-action"); + gtk_widget_set_visible (GTK_WIDGET (self->stat_label), TRUE); + gtk_stack_set_visible_child_name (self->view_stack, "browsing"); + sp_window_update_stats (self); + sp_window_update_subtitle (self); + sp_window_action_set (self, "close-capture", "enabled", TRUE, NULL); + sp_window_action_set (self, "save-capture", "enabled", TRUE, NULL); + + break; + + default: + g_warning ("Unknown state: %0d", state); + break; + } +} + +static void +sp_window_enable_stats (SpWindow *self) +{ + g_assert (SP_IS_WINDOW (self)); + + if (self->stats_handler == 0) + self->stats_handler = + g_timeout_add_seconds (1, sp_window_update_stats, self); +} + +static void +sp_window_disable_stats (SpWindow *self) +{ + g_assert (SP_IS_WINDOW (self)); + + if (self->stats_handler != 0) + { + g_source_remove (self->stats_handler); + self->stats_handler = 0; + } +} + +static void +sp_window_add_sources (SpWindow *window, + SpProfiler *profiler) +{ + g_autoptr(SpSource) proc_source = NULL; + g_autoptr(SpSource) perf_source = NULL; + + g_assert (SP_IS_WINDOW (window)); + g_assert (SP_IS_PROFILER (profiler)); + + proc_source = sp_proc_source_new (); + sp_profiler_add_source (profiler, proc_source); + + perf_source = sp_perf_source_new (); + sp_profiler_add_source (profiler, perf_source); +} + +static void +sp_window_start_recording (SpWindow *self) +{ + g_assert (SP_IS_WINDOW (self)); + + if (self->state == SP_WINDOW_STATE_RECORDING) + { + /* SpProfiler::stopped will move us to generating */ + gtk_label_set_label (self->subtitle, _("Stopping…")); + sp_profiler_stop (self->profiler); + return; + } + + if ((self->state == SP_WINDOW_STATE_EMPTY) || + (self->state == SP_WINDOW_STATE_FAILED) || + (self->state == SP_WINDOW_STATE_BROWSING)) + { + sp_window_add_sources (self, self->profiler); + sp_window_set_state (self, SP_WINDOW_STATE_RECORDING); + sp_window_enable_stats (self); + sp_profiler_start (self->profiler); + return; + } +} + +static void +sp_window_stop_recording (SpWindow *self) +{ + g_assert (SP_IS_WINDOW (self)); + + if (self->state == SP_WINDOW_STATE_RECORDING) + { + if (self->profiler != NULL) + sp_profiler_stop (self->profiler); + } +} + +static void +sp_window_hide_info_bar_revealer (SpWindow *self) +{ + g_assert (SP_IS_WINDOW (self)); + + gtk_revealer_set_reveal_child (self->info_bar_revealer, FALSE); +} + +static void +sp_window_profiler_stopped (SpWindow *self, + SpProfiler *profiler) +{ + g_autoptr(SpCaptureReader) reader = NULL; + g_autoptr(GError) error = NULL; + SpCaptureWriter *writer; + + g_assert (SP_IS_WINDOW (self)); + g_assert (SP_IS_PROFILER (profiler)); + + sp_window_disable_stats (self); + + if (self->state == SP_WINDOW_STATE_FAILED) + return; + + writer = sp_profiler_get_writer (profiler); + reader = sp_capture_writer_create_reader (writer, &error); + + if (reader == NULL) + { + sp_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", error->message); + sp_window_set_state (self, SP_WINDOW_STATE_EMPTY); + return; + } + + g_clear_pointer (&self->reader, sp_capture_reader_unref); + self->reader = g_steal_pointer (&reader); + + sp_window_build_profile (self); +} + +static void +sp_window_profiler_failed (SpWindow *self, + const GError *reason, + SpProfiler *profiler) +{ + g_assert (SP_IS_WINDOW (self)); + g_assert (reason != NULL); + g_assert (SP_IS_PROFILER (profiler)); + + sp_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", reason->message); + sp_window_set_state (self, SP_WINDOW_STATE_FAILED); +} + +static void +sp_window_set_profiler (SpWindow *self, + SpProfiler *profiler) +{ + g_assert (SP_IS_WINDOW (self)); + g_assert (SP_IS_PROFILER (profiler)); + + if (self->profiler != profiler) + { + if (self->profiler != NULL) + { + if (sp_profiler_get_is_running (self->profiler)) + sp_profiler_stop (self->profiler); + sp_profiler_menu_button_set_profiler (self->profiler_menu_button, NULL); + sp_recording_state_view_set_profiler (self->recording_view, NULL); + g_clear_object (&self->profiler); + } + + if (profiler != NULL) + { + if (!sp_profiler_get_is_mutable (profiler)) + { + g_warning ("Ignoring attempt to set profiler to an already running session!"); + return; + } + + self->profiler = g_object_ref (profiler); + + g_signal_connect_object (profiler, + "stopped", + G_CALLBACK (sp_window_profiler_stopped), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (profiler, + "failed", + G_CALLBACK (sp_window_profiler_failed), + self, + G_CONNECT_SWAPPED); + + sp_profiler_menu_button_set_profiler (self->profiler_menu_button, profiler); + sp_recording_state_view_set_profiler (self->recording_view, profiler); + } + } +} + +static void +sp_window_open_capture (GSimpleAction *action, + GVariant *variant, + gpointer user_data) +{ + SpWindow *self = user_data; + + g_assert (G_IS_SIMPLE_ACTION (action)); + g_assert (variant == NULL); + g_assert (SP_IS_WINDOW (self)); + + sp_window_open_from_dialog (self); +} + +static void +sp_window_save_capture (GSimpleAction *action, + GVariant *variant, + gpointer user_data) +{ + g_autoptr(SpCaptureReader) reader = NULL; + SpWindow *self = user_data; + GtkWidget *dialog; + GtkResponseType response; + + g_assert (G_IS_SIMPLE_ACTION (action)); + g_assert (variant == NULL); + g_assert (SP_IS_WINDOW (self)); + + if (self->reader == NULL) + return; + + reader = sp_capture_reader_ref (self->reader); + + dialog = gtk_file_chooser_dialog_new (_("Save Capture As"), + GTK_WINDOW (self), + GTK_FILE_CHOOSER_ACTION_SAVE, + _("Cancel"), GTK_RESPONSE_CANCEL, + _("Save"), GTK_RESPONSE_OK, + NULL); + + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response == GTK_RESPONSE_OK) + { + g_autofree gchar *filename = NULL; + g_autoptr(GError) error = NULL; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + + /* this should really be done outside the main loop. */ + if (!sp_capture_reader_save_as (reader, filename, &error)) + { + sp_window_notify_user (self, + GTK_MESSAGE_ERROR, + _("An error occurred while attempting to save your capture: %s"), + error->message); + goto failure; + } + } + +failure: + gtk_widget_destroy (dialog); +} + +static void +sp_window_close_capture (GSimpleAction *action, + GVariant *variant, + gpointer user_data) +{ + SpWindow *self = user_data; + + g_assert (G_IS_SIMPLE_ACTION (action)); + g_assert (variant == NULL); + g_assert (SP_IS_WINDOW (self)); + + sp_window_set_state (self, SP_WINDOW_STATE_EMPTY); +} + +static void +sp_window_record_button_clicked (SpWindow *self, + GtkButton *button) +{ + g_assert (SP_IS_WINDOW (self)); + g_assert (GTK_IS_BUTTON (button)); + + sp_window_start_recording (self); +} + +static void +sp_window_destroy (GtkWidget *widget) +{ + SpWindow *self = (SpWindow *)widget; + + g_clear_object (&self->profiler); + g_clear_pointer (&self->reader, sp_capture_reader_unref); + sp_window_disable_stats (self); + + GTK_WIDGET_CLASS (sp_window_parent_class)->destroy (widget); +} + +static void +sp_window_constructed (GObject *object) +{ + SpWindow *self = (SpWindow *)object; + g_autoptr(SpProfiler) profiler = NULL; + + G_OBJECT_CLASS (sp_window_parent_class)->constructed (object); + + profiler = sp_profiler_new (); + sp_window_set_profiler (self, profiler); + + sp_window_set_state (self, SP_WINDOW_STATE_EMPTY); +} + +static void +sp_window_class_init (SpWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkBindingSet *binding_set; + + object_class->constructed = sp_window_constructed; + + widget_class->destroy = sp_window_destroy; + + signals [START_RECORDING] = + g_signal_new_class_handler ("start-recording", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (sp_window_start_recording), + NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals [STOP_RECORDING] = + g_signal_new_class_handler ("stop-recording", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (sp_window_stop_recording), + NULL, NULL, NULL, G_TYPE_NONE, 0); + + binding_set = gtk_binding_set_by_class (klass); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "stop-recording", 0); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sp-window.ui"); + gtk_widget_class_bind_template_child (widget_class, SpWindow, callgraph_view); + gtk_widget_class_bind_template_child (widget_class, SpWindow, empty_view); + gtk_widget_class_bind_template_child (widget_class, SpWindow, gear_menu_button); + gtk_widget_class_bind_template_child (widget_class, SpWindow, info_bar); + gtk_widget_class_bind_template_child (widget_class, SpWindow, info_bar_label); + gtk_widget_class_bind_template_child (widget_class, SpWindow, info_bar_revealer); + gtk_widget_class_bind_template_child (widget_class, SpWindow, profiler_menu_button); + gtk_widget_class_bind_template_child (widget_class, SpWindow, record_button); + gtk_widget_class_bind_template_child (widget_class, SpWindow, recording_view); + gtk_widget_class_bind_template_child (widget_class, SpWindow, stat_label); + gtk_widget_class_bind_template_child (widget_class, SpWindow, subtitle); + gtk_widget_class_bind_template_child (widget_class, SpWindow, title); + gtk_widget_class_bind_template_child (widget_class, SpWindow, view_stack); +} + +static void +sp_window_init (SpWindow *self) +{ + GAction *action; + static GActionEntry action_entries[] = { + { "close-capture", sp_window_close_capture }, + { "open-capture", sp_window_open_capture }, + { "save-capture", sp_window_save_capture }, + }; + GtkApplication *app; + GMenu *menu; + + gtk_widget_init_template (GTK_WIDGET (self)); + + /* + * Hookup widget signals. + */ + + g_signal_connect_object (self->info_bar, + "response", + G_CALLBACK (sp_window_hide_info_bar_revealer), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (self->info_bar, + "close", + G_CALLBACK (sp_window_hide_info_bar_revealer), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (self->record_button, + "clicked", + G_CALLBACK (sp_window_record_button_clicked), + self, + G_CONNECT_SWAPPED); + + /* + * Setup actions for the window. + */ + + g_action_map_add_action_entries (G_ACTION_MAP (self), + action_entries, + G_N_ELEMENTS (action_entries), + self); + + /* + * Setup our gear (hamburger) menu. + */ + + app = GTK_APPLICATION (g_application_get_default ()); + menu = gtk_application_get_menu_by_id (app, "gear-menu"); + gtk_menu_button_set_menu_model (self->gear_menu_button, G_MENU_MODEL (menu)); + + /* + * Restore previous window settings. + */ + + sp_window_settings_register (GTK_WINDOW (self)); + + /* + * Set default focus to the record button for quick workflow of + * launch, enter, escape, view. + */ + gtk_window_set_focus (GTK_WINDOW (self), GTK_WIDGET (self->record_button)); +} + +static void +sp_window_open_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SpWindow *self = (SpWindow *)object; + g_autoptr(SpCaptureReader) reader = NULL; + g_autoptr(GError) error = NULL; + + g_assert (SP_IS_WINDOW (self)); + g_assert (G_IS_TASK (result)); + + reader = g_task_propagate_pointer (G_TASK (result), &error); + + if (reader == NULL) + { + sp_window_notify_user (self, + GTK_MESSAGE_ERROR, + "%s", error->message); + return; + } + + g_clear_pointer (&self->reader, sp_capture_reader_unref); + self->reader = g_steal_pointer (&reader); + + sp_window_set_state (self, SP_WINDOW_STATE_PROCESSING); +} + +static void +sp_window_open_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autofree gchar *path = NULL; + SpCaptureReader *reader; + GFile *file = task_data; + GError *error = NULL; + + g_assert (G_IS_TASK (task)); + g_assert (SP_IS_WINDOW (source_object)); + g_assert (G_IS_FILE (file)); + + path = g_file_get_path (file); + + if (NULL == (reader = sp_capture_reader_new (path, &error))) + { + g_task_return_error (task, error); + return; + } + + g_task_return_pointer (task, reader, (GDestroyNotify)sp_capture_reader_unref); +} + +void +sp_window_open (SpWindow *self, + GFile *file) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SP_IS_WINDOW (self)); + g_return_if_fail (G_IS_FILE (file)); + + if (!g_file_is_native (file)) + { + sp_window_notify_user (self, + GTK_MESSAGE_ERROR, + _("The file \"%s\" could not be opened. Only local files are supported."), + g_file_get_uri (file)); + return; + } + + task = g_task_new (self, NULL, sp_window_open_cb, NULL); + g_task_set_task_data (task, g_object_ref (file), g_object_unref); + g_task_run_in_thread (task, sp_window_open_worker); +} + +SpWindowState +sp_window_get_state (SpWindow *self) +{ + g_return_val_if_fail (SP_IS_WINDOW (self), SP_WINDOW_STATE_0); + + return self->state; +} + +void +sp_window_open_from_dialog (SpWindow *self) +{ + GtkFileFilter *filter; + GtkDialog *dialog; + + g_assert (SP_IS_WINDOW (self)); + + dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG, + "action", GTK_FILE_CHOOSER_ACTION_OPEN, + "title", _("Open Capture"), + "transient-for", self, + NULL); + + gtk_dialog_add_buttons (dialog, + _("Cancel"), GTK_RESPONSE_CANCEL, + _("Open"), GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Sysprof Captures")); + gtk_file_filter_add_pattern (filter, "*.syscap"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + if (gtk_dialog_run (dialog) == GTK_RESPONSE_OK) + { + g_autoptr(GFile) file = NULL; + + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + sp_window_open (self, file); + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} diff --git a/src/sp-window.h b/src/sp-window.h new file mode 100644 index 00000000..5f6bcb37 --- /dev/null +++ b/src/sp-window.h @@ -0,0 +1,52 @@ +/* sp-window.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 SP_WINDOW_H +#define SP_WINDOW_H + +#include + +G_BEGIN_DECLS + +#define SP_TYPE_WINDOW (sp_window_get_type()) + +G_DECLARE_FINAL_TYPE (SpWindow, sp_window, SP, WINDOW, GtkApplicationWindow) + +typedef enum +{ + SP_WINDOW_STATE_0, + SP_WINDOW_STATE_EMPTY, + SP_WINDOW_STATE_FAILED, + SP_WINDOW_STATE_RECORDING, + SP_WINDOW_STATE_PROCESSING, + SP_WINDOW_STATE_BROWSING, +} SpWindowState; + +SpWindowState sp_window_get_state (SpWindow *self); +gboolean sp_window_get_recording (SpWindow *self); +GFile *sp_window_get_capture_file (SpWindow *self); +void sp_window_set_capture_file (SpWindow *self, + GFile *capture_file); +void sp_window_open (SpWindow *self, + GFile *file); +void sp_window_open_from_dialog (SpWindow *self); + +G_END_DECLS + +#endif /* SP_WINDOW_H */ + diff --git a/src/sysprof.c b/src/sysprof.c index 8fec7f70..1ad81584 100644 --- a/src/sysprof.c +++ b/src/sysprof.c @@ -1,10 +1,10 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, 2006, 2007, 2008, Soeren Sandmann +/* main.c * - * This program is free software; you can redistribute it and/or modify + * Copyright (C) 2016 Christian Hergert + * + * 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 + * 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, @@ -13,1723 +13,26 @@ * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program. If not, see . */ -#include +#include #include -#include -#include -#include -#include -#include +#include -#include "footreestore.h" -#include "treeviewutils.h" -#include "profile.h" -#include "collector.h" +#include "sp-application.h" -/* FIXME - not10 */ -#define _(a) a - -#define APPLICATION_NAME "System Profiler" - -typedef struct Application Application; - -typedef enum +gint +main (gint argc, + gchar *argv[]) { - INITIAL, - DISPLAYING, - PROFILING -} State; + g_autoptr(SpApplication) app = NULL; + gint ret; -struct Application -{ - Collector * collector; + sp_clock_init (); - State state; - GdkPixbuf * icon; + app = sp_application_new (); + ret = g_application_run (G_APPLICATION (app), argc, argv); - GtkWidget * main_window; - - GtkTreeView * object_view; - GtkTreeView * callers_view; - GtkTreeView * descendants_view; - - GtkWidget * start_button; - GtkWidget * profile_button; - GtkWidget * reset_button; - GtkWidget * save_as_button; - GtkWidget * dummy_button; - - GtkWidget * start_item; - GtkWidget * profile_item; - GtkWidget * reset_item; - GtkWidget * save_as_item; - GtkWidget * open_item; - GtkWidget * screenshot_item; - GtkWidget * about_item; - GtkWidget * quit_item; - GtkWidget * hpaned; - GtkWidget * vpaned; - - GtkTreeSelection * object_selection; - - GtkWidget * samples_label; - GtkWidget * samples_hbox; - - gboolean screenshot_window_visible; - GtkWidget * screenshot_textview; - GtkWidget * screenshot_close_button; - GtkWidget * screenshot_window; - - Profile * profile; - ProfileDescendant * descendants; - ProfileCaller * callers; - - int timeout_id; - int update_screenshot_id; - - char * loaded_profile; - - gboolean inhibit_forced_redraw; -}; - -static void update_screenshot_window (Application *app); - -static void -show_samples (Application *app) -{ - char *label; - int n_samples; - - switch (app->state) - { - case INITIAL: - n_samples = 0; - break; - - case PROFILING: - n_samples = collector_get_n_samples (app->collector); - break; - - case DISPLAYING: - n_samples = profile_get_size (app->profile); - break; - - default: - g_assert_not_reached(); - break; - } - - label = g_strdup_printf ("%d", n_samples); - - gtk_label_set_label (GTK_LABEL (app->samples_label), label); - - g_free (label); -} - -static gboolean -show_samples_timeout (gpointer data) -{ - Application *app = data; - - show_samples (app); - - app->timeout_id = 0; - - return FALSE; -} - -static void -queue_show_samples (Application *app) -{ - if (!app->timeout_id) - app->timeout_id = g_timeout_add (225, show_samples_timeout, app); -} - -static void -update_sensitivity (Application *app) -{ - gboolean sensitive_profile_button; - gboolean sensitive_save_as_button; - gboolean sensitive_start_button; - gboolean sensitive_tree_views; - gboolean sensitive_samples_hbox; - gboolean sensitive_reset_button; - - GtkWidget *active_radio_button; - - gboolean has_samples; - - switch (app->state) - { - case INITIAL: - sensitive_profile_button = FALSE; - sensitive_save_as_button = FALSE; - sensitive_start_button = TRUE; - sensitive_reset_button = FALSE; - sensitive_tree_views = FALSE; - sensitive_samples_hbox = FALSE; - active_radio_button = app->dummy_button; - break; - - case PROFILING: - has_samples = (collector_get_n_samples (app->collector) > 0); - - sensitive_profile_button = has_samples; - sensitive_save_as_button = has_samples; - sensitive_reset_button = has_samples; - sensitive_start_button = TRUE; - sensitive_tree_views = FALSE; - sensitive_samples_hbox = TRUE; - active_radio_button = app->start_button; - break; - - case DISPLAYING: - sensitive_profile_button = TRUE; - sensitive_save_as_button = TRUE; - sensitive_start_button = TRUE; - sensitive_tree_views = TRUE; - sensitive_reset_button = TRUE; - sensitive_samples_hbox = FALSE; - active_radio_button = app->profile_button; - break; - - default: - g_assert_not_reached(); - break; - } - - gtk_toggle_tool_button_set_active ( - GTK_TOGGLE_TOOL_BUTTON (active_radio_button), TRUE); - - /* "profile" widgets */ - gtk_widget_set_sensitive (GTK_WIDGET (app->profile_button), - sensitive_profile_button); - gtk_widget_set_sensitive (GTK_WIDGET (app->profile_item), - sensitive_profile_button); - - /* "save as" widgets */ - gtk_widget_set_sensitive (GTK_WIDGET (app->save_as_button), - sensitive_save_as_button); - gtk_widget_set_sensitive (app->save_as_item, - sensitive_save_as_button); - - /* "start" widgets */ - gtk_widget_set_sensitive (GTK_WIDGET (app->start_button), - sensitive_start_button); - gtk_widget_set_sensitive (GTK_WIDGET (app->start_item), - sensitive_start_button); - -#if 0 - /* FIXME - not10: gtk+ doesn't handle changes in sensitivity in response - * to a click on the same button very well - */ - gtk_widget_set_sensitive (GTK_WIDGET (app->reset_button), - sensitive_reset_button); - gtk_widget_set_sensitive (GTK_WIDGET (app->reset_item), - sensitive_reset_button); -#endif - - gtk_widget_set_sensitive (GTK_WIDGET (app->object_view), sensitive_tree_views); - gtk_widget_set_sensitive (GTK_WIDGET (app->callers_view), sensitive_tree_views); - gtk_widget_set_sensitive (GTK_WIDGET (app->descendants_view), sensitive_tree_views); - gtk_widget_set_sensitive (GTK_WIDGET (app->samples_hbox), sensitive_samples_hbox); - - if (app->screenshot_window_visible) - gtk_widget_show (app->screenshot_window); - else - gtk_widget_hide (app->screenshot_window); - - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (app->screenshot_item), - app->screenshot_window_visible); - - show_samples (app); -} - -static void -set_busy (GtkWidget *widget, - gboolean busy) -{ - GdkCursor *cursor; - GdkWindow *window; - - if (busy) - cursor = gdk_cursor_new (GDK_WATCH); - else - cursor = NULL; - - if (GTK_IS_TEXT_VIEW (widget)) - window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT); - else - window = widget->window; - - gdk_window_set_cursor (window, cursor); - - if (cursor) - gdk_cursor_unref (cursor); - - gdk_flush(); -} - -static void -set_application_title (Application *app, - const char * name) -{ - char *new_name; - if (name) - new_name = g_path_get_basename (name); - else - new_name = NULL; - - if (app->loaded_profile) - g_free (app->loaded_profile); - - app->loaded_profile = new_name; - - if (app->loaded_profile) - { - gtk_window_set_title (GTK_WINDOW (app->main_window), - app->loaded_profile); - } - else - { - gtk_window_set_title (GTK_WINDOW (app->main_window), - APPLICATION_NAME); - } -} - -static void -delete_data (Application *app) -{ - if (app->profile) - { - profile_free (app->profile); - app->profile = NULL; - - gtk_tree_view_set_model (GTK_TREE_VIEW (app->object_view), NULL); - gtk_tree_view_set_model (GTK_TREE_VIEW (app->callers_view), NULL); - gtk_tree_view_set_model (GTK_TREE_VIEW (app->descendants_view), NULL); - } - - collector_reset (app->collector); - - set_application_title (app, NULL); -} - -static void -sorry (GtkWidget *parent_window, - const gchar *format, - ...) -{ - va_list args; - char *message; - GtkWidget *dialog; - - va_start (args, format); - g_vasprintf (&message, format, args); - va_end (args); - - dialog = gtk_message_dialog_new (parent_window ? GTK_WINDOW (parent_window) : NULL, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_OK, "%s", message); - g_free (message); - - gtk_window_set_title (GTK_WINDOW (dialog), APPLICATION_NAME " Warning"); - - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); -} - -static void -on_menu_item_activated (GtkWidget *menu_item, GtkWidget *tool_button) -{ - GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON (tool_button); - - if (!gtk_toggle_tool_button_get_active (button)) - gtk_toggle_tool_button_set_active (button, TRUE); -} - -static void -on_start_toggled (GtkWidget *widget, gpointer data) -{ - Application *app = data; - GError *err = NULL; - - if (!gtk_toggle_tool_button_get_active ( - GTK_TOGGLE_TOOL_BUTTON (app->start_button))) - { - return; - } - - if (collector_start (app->collector, -1, &err)) - { - delete_data (app); - - app->state = PROFILING; - } - else - { - sorry (app->main_window, err->message); - - g_error_free (err); - } - - update_screenshot_window (app); - update_sensitivity (app); -} - -enum -{ - OBJECT_NAME, - OBJECT_SELF, - OBJECT_TOTAL, - OBJECT_OBJECT -}; - -enum -{ - CALLERS_NAME, - CALLERS_SELF, - CALLERS_TOTAL, - CALLERS_OBJECT -}; - -enum -{ - DESCENDANTS_NAME, - DESCENDANTS_SELF, - DESCENDANTS_CUMULATIVE, - DESCENDANTS_OBJECT -}; - -static char * -get_current_object (Application *app) -{ - GtkTreeModel *model; - GtkTreeIter selected; - char *object; - - if (gtk_tree_selection_get_selected (app->object_selection, &model, &selected)) - { - gtk_tree_model_get (model, &selected, - OBJECT_OBJECT, &object, - -1); - return object; - } - else - { - return NULL; - } -} - -static void -fill_main_list (Application *app) -{ - GList *list; - GtkListStore *list_store; - Profile *profile = app->profile; - GList *objects; - - if (profile) - { - list_store = gtk_list_store_new (4, - G_TYPE_STRING, - G_TYPE_DOUBLE, - G_TYPE_DOUBLE, - G_TYPE_POINTER); - - objects = profile_get_objects (profile); - for (list = objects; list != NULL; list = list->next) - { - ProfileObject *object = list->data; - GtkTreeIter iter; - double profile_size = profile_get_size (profile); - - gtk_list_store_append (list_store, &iter); - - gtk_list_store_set (list_store, &iter, - OBJECT_NAME, object->name, - OBJECT_SELF, 100.0 * object->self / profile_size, - OBJECT_TOTAL, 100.0 * object->total / profile_size, -#if 0 - OBJECT_SELF, (double)object->self, - OBJECT_TOTAL, (double)object->total, -#endif - OBJECT_OBJECT, object->name, - -1); - } - g_list_foreach (objects, (GFunc)g_free, NULL); - g_list_free (objects); - - tree_view_set_model_with_default_sort (app->object_view, GTK_TREE_MODEL (list_store), - OBJECT_TOTAL, GTK_SORT_DESCENDING); - - g_object_unref (G_OBJECT (list_store)); - } - - gtk_tree_view_columns_autosize (app->object_view); -} - -static void -add_node (FooTreeStore *store, - int size, - const GtkTreeIter *parent, - ProfileDescendant *node) -{ - GtkTreeIter iter; - - if (!node) - return; - - foo_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0); - - foo_tree_store_set (store, &iter, - DESCENDANTS_NAME, node->name, - DESCENDANTS_SELF, 100 * (node->self)/(double)size, - DESCENDANTS_CUMULATIVE, 100 * (node->cumulative)/(double)size, -#if 0 - DESCENDANTS_SELF, (double)node->self, - DESCENDANTS_CUMULATIVE, (double)node->non_recursion, -#endif - DESCENDANTS_OBJECT, node->name, - -1); - - add_node (store, size, parent, node->siblings); - add_node (store, size, &iter, node->children); -} - -static void -fill_descendants_tree (Application *app) -{ - FooTreeStore *tree_store; - - if (app->descendants) - { - profile_descendant_free (app->descendants); - app->descendants = NULL; - } - - tree_store = - foo_tree_store_new (4, - G_TYPE_STRING, - G_TYPE_DOUBLE, - G_TYPE_DOUBLE, - G_TYPE_POINTER); - - if (app->profile) - { - char *object = get_current_object (app); - if (object) - { - app->descendants = - profile_create_descendants (app->profile, object); - add_node (tree_store, - profile_get_size (app->profile), NULL, app->descendants); - } - } - - tree_view_set_model_with_default_sort (app->descendants_view, GTK_TREE_MODEL (tree_store), - DESCENDANTS_CUMULATIVE, GTK_SORT_DESCENDING); - - g_object_unref (G_OBJECT (tree_store)); - - gtk_tree_view_columns_autosize (app->descendants_view); -} - -static void -add_callers (GtkListStore *list_store, - Profile *profile, - ProfileCaller *callers) -{ - while (callers) - { - gchar *name; - GtkTreeIter iter; - double profile_size = profile_get_size (profile); - - if (callers->name) - name = callers->name; - else - name = ""; - - gtk_list_store_append (list_store, &iter); - gtk_list_store_set ( - list_store, &iter, - CALLERS_NAME, name, - CALLERS_SELF, 100.0 * callers->self / profile_size, - CALLERS_TOTAL, 100.0 * callers->total / profile_size, -#if 0 - CALLERS_SELF, (double)callers->self, - CALLERS_TOTAL, (double)callers->total, -#endif - CALLERS_OBJECT, callers->name, - -1); - - callers = callers->next; - } -} - -static void -fill_callers_list (Application *app) -{ - GtkListStore *list_store; - - if (app->callers) - { - profile_caller_free (app->callers); - app->callers = NULL; - } - - list_store = - gtk_list_store_new (4, - G_TYPE_STRING, - G_TYPE_DOUBLE, - G_TYPE_DOUBLE, - G_TYPE_POINTER); - - if (app->profile) - { - char *object = get_current_object (app); - if (object) - { - app->callers = profile_list_callers (app->profile, object); - add_callers (list_store, app->profile, app->callers); - } - } - - tree_view_set_model_with_default_sort (app->callers_view, GTK_TREE_MODEL (list_store), - CALLERS_TOTAL, GTK_SORT_DESCENDING); - - g_object_unref (G_OBJECT (list_store)); - - gtk_tree_view_columns_autosize (app->callers_view); -} - -static void -enter_display_mode (Application *app) -{ - app->state = DISPLAYING; - - update_sensitivity (app); - - app->inhibit_forced_redraw = TRUE; - - fill_main_list (app); - - /* This has the side effect of selecting the first row, which in turn causes - * the other lists to be filled out - */ - gtk_widget_grab_focus (GTK_WIDGET (app->object_view)); - - app->inhibit_forced_redraw = FALSE; -} - -static void -ensure_profile (Application *app) -{ - if (app->profile) - return; - - collector_stop (app->collector); - - app->profile = collector_create_profile (app->collector); - - collector_reset (app->collector); - - enter_display_mode (app); -} - -static void -on_about_activated (GtkWidget *widget, gpointer data) -{ -#define OSLASH "\303\270" - Application *app = data; - char *name_property; - - if (gtk_minor_version >= 12) - name_property = "program-name"; - else - name_property = "name"; - - gtk_show_about_dialog (GTK_WINDOW (app->main_window), - "logo", app->icon, - name_property, APPLICATION_NAME, - "copyright", "Copyright 2004-2009, S"OSLASH"ren Sandmann", - "version", PACKAGE_VERSION, - NULL); -} - -static void -on_profile_toggled (GtkWidget *widget, gpointer data) -{ - Application *app = data; - - if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (app->profile_button))) - { - set_busy (app->main_window, TRUE); - ensure_profile (app); - set_busy (app->main_window, FALSE); - } -} - -static void -on_reset_clicked (gpointer widget, gpointer data) -{ - Application *app = data; - - set_busy (app->main_window, TRUE); - - delete_data (app); - - if (app->state == DISPLAYING) - { - app->state = INITIAL; - collector_stop (app->collector); - } - - update_sensitivity (app); - - set_busy (app->main_window, FALSE); -} - -static gboolean -overwrite_file (GtkWindow *window, - const char *filename) -{ - GtkWidget *msgbox; - gchar *utf8_file_name; - AtkObject *obj; - gint ret; - - utf8_file_name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); - msgbox = gtk_message_dialog_new (window, - (GtkDialogFlags)GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_NONE, - _("A file named \"%s\" already exists."), - utf8_file_name); - g_free (utf8_file_name); - - gtk_message_dialog_format_secondary_text ( - GTK_MESSAGE_DIALOG (msgbox), - _("Do you want to replace it with the one you are saving?")); - - gtk_dialog_add_button (GTK_DIALOG (msgbox), - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); - - gtk_dialog_add_button (GTK_DIALOG (msgbox), - _("_Replace"), GTK_RESPONSE_YES); - - gtk_dialog_set_default_response (GTK_DIALOG (msgbox), - GTK_RESPONSE_CANCEL); - - obj = gtk_widget_get_accessible (msgbox); - - if (GTK_IS_ACCESSIBLE (obj)) - atk_object_set_name (obj, _("Question")); - - ret = gtk_dialog_run (GTK_DIALOG (msgbox)); - gtk_widget_destroy (msgbox); - - return (ret == GTK_RESPONSE_YES); -} - -static void -on_save_as_clicked (gpointer widget, - gpointer data) -{ - Application *app = data; - GtkWidget *dialog; - - ensure_profile (app); - - set_busy (app->main_window, TRUE); - - dialog = gtk_file_chooser_dialog_new ("Save As", - GTK_WINDOW (app->main_window), - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - - gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); - gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - - set_busy (app->main_window, FALSE); - -retry: - if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) - { - GError *err = NULL; - gchar *filename; - - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - - if (g_file_test (filename, G_FILE_TEST_EXISTS) && - !overwrite_file (GTK_WINDOW (app->main_window), filename)) - { - g_free (filename); - goto retry; - } - - set_busy (dialog, TRUE); - if (!profile_save (app->profile, filename, &err)) - { - sorry (app->main_window, "Could not save %s: %s", - filename, err->message); - - set_busy (dialog, FALSE); - g_free (filename); - goto retry; - } - set_application_title (app, filename); - set_busy (dialog, FALSE); - g_free (filename); - } - - gtk_widget_destroy (dialog); -} - -static void -set_loaded_profile (Application *app, - const char *name, - Profile *profile) -{ - g_return_if_fail (name != NULL); - g_return_if_fail (profile != NULL); - - collector_stop (app->collector); - - delete_data (app); - - app->profile = profile; - - set_application_title (app, name); - - enter_display_mode (app); -} - -static void -show_could_not_open (Application *app, - const char *filename, - GError *err) -{ - sorry (app->main_window, - "Could not open %s: %s", - filename, - err->message); -} - -static void -on_open_clicked (gpointer widget, - gpointer data) -{ - Application *app = data; - gchar *filename = NULL; - Profile *profile = NULL; - GtkWidget *dialog; - - set_busy (app->main_window, TRUE); - - dialog = gtk_file_chooser_dialog_new ("Open", - GTK_WINDOW (app->main_window), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - - gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - - set_busy (app->main_window, FALSE); - -retry: - if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) - { - GError *err = NULL; - - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - - set_busy (dialog, TRUE); - - profile = profile_load (filename, &err); - - if (!profile) - { - set_busy (dialog, FALSE); - - show_could_not_open (app, filename, err); - g_error_free (err); - g_free (filename); - - filename = NULL; - goto retry; - } - - set_busy (dialog, FALSE); - } - - gtk_widget_destroy (dialog); - - if (profile) - { - g_assert (filename); - set_loaded_profile (app, filename, profile); - - g_free (filename); - } -} - -static void -on_delete (GtkWidget *window, - Application *app) -{ - /* Workaround for http://bugzilla.gnome.org/show_bug.cgi?id=317775 - * - * Without it, the read callbacks can fire _after_ gtk_main_quit() - * has been called and cause stuff to be called on destroyed widgets. - */ - while (gtk_main_iteration ()) - ; - - gtk_main_quit (); -} - -static void -expand_descendants_tree (Application *app) -{ - GtkTreeModel *model = gtk_tree_view_get_model (app->descendants_view); - GtkTreeIter iter; - GList *all_paths = NULL; - int n_rows; - int max_rows = 40; /* FIXME */ - double top_value = 0.0; - GtkTreePath *first_path; - GList *list; - - first_path = gtk_tree_path_new_first(); - - all_paths = g_list_prepend (all_paths, first_path); - - n_rows = 1; - - gtk_tree_model_get_iter (model, &iter, first_path); - gtk_tree_model_get (model, &iter, - OBJECT_TOTAL, &top_value, - -1); - - while (all_paths && n_rows < max_rows) - { - GtkTreeIter best_iter; - GtkTreePath *best_path; - double best_value; - int n_children; - int i; - - best_value = 0.0; - best_path = NULL; - - for (list = all_paths; list != NULL; list = list->next) - { - GtkTreePath *path = list->data; - GtkTreeIter iter; - - g_assert (path != NULL); - - if (gtk_tree_model_get_iter (model, &iter, path)) - { - double value; - gtk_tree_model_get (model, &iter, - OBJECT_TOTAL, &value, - -1); - - if (value >= best_value) - { - best_value = value; - best_path = path; - best_iter = iter; - } - } - } - - n_children = gtk_tree_model_iter_n_children (model, &best_iter); - - if (n_children && (best_value / top_value) > 0.04 && - (n_children + gtk_tree_path_get_depth (best_path)) / - (double)max_rows < (best_value / top_value) ) - { - gtk_tree_view_expand_row ( - GTK_TREE_VIEW (app->descendants_view), best_path, FALSE); - n_rows += n_children; - - if (gtk_tree_path_get_depth (best_path) < 4) - { - GtkTreePath *path = gtk_tree_path_copy (best_path); - gtk_tree_path_down (path); - - for (i = 0; i < n_children; ++i) - { - all_paths = g_list_prepend (all_paths, path); - - path = gtk_tree_path_copy (path); - gtk_tree_path_next (path); - } - - gtk_tree_path_free (path); - } - } - - all_paths = g_list_remove (all_paths, best_path); - - if (!all_paths && n_rows == 1) - { - /* Always expand at least once */ - gtk_tree_view_expand_row (GTK_TREE_VIEW (app->descendants_view), - best_path, FALSE); - } - - gtk_tree_path_free (best_path); - } - - for (list = all_paths; list != NULL; list = list->next) - gtk_tree_path_free (list->data); - - g_list_free (all_paths); -} - -static void -get_data (GtkTreeView *view, - GtkTreeIter *iter, - gchar **name, - double *self, - double *cumulative) -{ - char *dummy1; - double dummy2; - double dummy3; - - GtkTreeModel *model = gtk_tree_view_get_model (view); - gtk_tree_model_get ( - model, iter, - DESCENDANTS_NAME, name? name : &dummy1, - DESCENDANTS_SELF, self? self : &dummy2, - DESCENDANTS_CUMULATIVE, cumulative? cumulative : &dummy3, - -1); -} - -static int -get_indent (GtkTreePath *path) -{ - return 2 * (gtk_tree_path_get_depth (path) - 1); -} - -static void -compute_text_width (GtkTreeView *view, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - int *width = data; - char *name; - - get_data (view, iter, &name, NULL, NULL); - - *width = MAX (g_utf8_strlen (name, -1) + get_indent (path), *width); - - g_free (name); -} - -typedef struct -{ - int max_width; - GString *text; -} AddTextInfo; - -static void -set_monospace (GtkWidget *widget) -{ - PangoFontDescription *desc = - pango_font_description_from_string ("monospace"); - - gtk_widget_modify_font (widget, desc); - - pango_font_description_free (desc); -} - -static void -add_text (GtkTreeView *view, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - AddTextInfo *info = data; - char *name; - double self; - double cumulative; - int indent; - int i; - - get_data (view, iter, &name, &self, &cumulative); - - indent = get_indent (path); - - for (i = 0; i < indent; ++i) - g_string_append_c (info->text, ' '); - - g_string_append_printf (info->text, "%-*s %6.2f%% %6.2f%%\n", - info->max_width - indent, name, self, cumulative); - - g_free (name); -} - -static gboolean -update_screenshot_window_idle (gpointer data) -{ - Application *app = data; - GtkTextBuffer *text_buffer; - - if (!app->screenshot_window_visible) - return FALSE; - - text_buffer = - gtk_text_view_get_buffer (GTK_TEXT_VIEW (app->screenshot_textview)); - - gtk_text_buffer_set_text (text_buffer, "", -1); - - if (app->descendants) - { - AddTextInfo info; - - info.max_width = 0; - info.text = g_string_new (""); - - tree_view_foreach_visible (app->descendants_view, - compute_text_width, - &info.max_width); - - tree_view_foreach_visible (app->descendants_view, - add_text, - &info); - - gtk_text_buffer_set_text (text_buffer, info.text->str, -1); - - set_monospace (app->screenshot_textview); - - g_string_free (info.text, TRUE); - } - - app->update_screenshot_id = 0; - - if (app->screenshot_window_visible) - { - set_busy (app->screenshot_window, FALSE); - set_busy (app->screenshot_textview, FALSE); - } - - return FALSE; -} - -static void -update_screenshot_window (Application *app) -{ - /* We do this in an idle handler to deal with the case where - * someone presses Shift-RightArrow on the root of a huge - * profile. This causes a ton of 'expanded' notifications, - * each of which would cause us to traverse the tree and - * update the screenshot window. - */ - if (app->update_screenshot_id) - g_source_remove (app->update_screenshot_id); - - if (app->screenshot_window_visible) - { - /* don't swamp the X server with cursor change requests */ - if (!app->update_screenshot_id) - { - set_busy (app->screenshot_window, TRUE); - set_busy (app->screenshot_textview, TRUE); - } - } - - app->update_screenshot_id = g_idle_add ( - update_screenshot_window_idle, app); -} - -static void -on_descendants_row_expanded_or_collapsed (GtkTreeView *tree, - GtkTreeIter *iter, - GtkTreePath *path, - Application *app) -{ - update_screenshot_window (app); -} - -static void -on_object_selection_changed (GtkTreeSelection *selection, - gpointer data) -{ - Application *app = data; - - set_busy (app->main_window, TRUE); - - update_screenshot_window (app); - - if (!app->inhibit_forced_redraw) - gdk_window_process_all_updates (); /* Display updated selection */ - - fill_descendants_tree (app); - fill_callers_list (app); - - if (get_current_object (app)) - expand_descendants_tree (app); - - set_busy (app->main_window, FALSE); -} - -static void -really_goto_object (Application *app, - char *object) -{ - GtkTreeModel *profile_objects; - GtkTreeIter iter; - gboolean found = FALSE; - - profile_objects = gtk_tree_view_get_model (app->object_view); - - if (gtk_tree_model_get_iter_first (profile_objects, &iter)) - { - do - { - char *list_object; - - gtk_tree_model_get (profile_objects, &iter, - OBJECT_OBJECT, &list_object, - -1); - - if (list_object == object) - { - found = TRUE; - break; - } - } - while (gtk_tree_model_iter_next (profile_objects, &iter)); - } - - if (found) - { - GtkTreePath *path = - gtk_tree_model_get_path (profile_objects, &iter); - - gtk_tree_view_set_cursor (app->object_view, path, 0, FALSE); - } -} - -static void -goto_object (Application *app, - GtkTreeView *tree_view, - GtkTreePath *path, - gint column) -{ - GtkTreeIter iter; - GtkTreeModel *model = gtk_tree_view_get_model (tree_view); - char *object; - - if (!gtk_tree_model_get_iter (model, &iter, path)) - return; - - gtk_tree_model_get (model, &iter, column, &object, -1); - - if (!object) - return; - - really_goto_object (app, object); -} - -static void -on_descendants_row_activated (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gpointer data) -{ - Application *app = data; - - goto_object (app, tree_view, path, DESCENDANTS_OBJECT); - - gtk_widget_grab_focus (GTK_WIDGET (app->descendants_view)); -} - -static void -on_callers_row_activated (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gpointer data) -{ - Application *app = data; - - goto_object (app, tree_view, path, CALLERS_OBJECT); - - gtk_widget_grab_focus (GTK_WIDGET (app->callers_view)); -} - -static void -on_screenshot_activated (GtkCheckMenuItem *menu_item, - Application *app) -{ - app->screenshot_window_visible = gtk_check_menu_item_get_active (menu_item); - - update_screenshot_window (app); - - update_sensitivity (app); -} - -static void -on_screenshot_window_delete (GtkWidget *window, - GdkEvent *event, - Application *app) -{ - app->screenshot_window_visible = FALSE; - - update_sensitivity (app); -} - -static void -on_screenshot_close_button_clicked (GtkWidget *widget, - Application *app) -{ - app->screenshot_window_visible = FALSE; - - update_sensitivity (app); -} - -static void -set_sizes (GtkWindow *window, - GtkWindow *screenshot_window, - GtkWidget *hpaned, - GtkWidget *vpaned) -{ - GdkScreen *screen; - int monitor_num; - GdkRectangle monitor; - int width, height; - GtkWidget *widget = GTK_WIDGET (window); - - screen = gtk_widget_get_screen (widget); - monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window); - - gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); - - width = monitor.width * 3 / 4; - height = monitor.height * 3 / 4; - - gtk_window_resize (window, width, height); - - gtk_paned_set_position (GTK_PANED (vpaned), height / 2); - gtk_paned_set_position (GTK_PANED (hpaned), width * 3 / 8); - - width = monitor.width * 5 / 8; - height = monitor.height * 5 / 8; - - gtk_window_resize (screenshot_window, width, height); -} - -#define GLADE_FILE DATADIR "/sysprof.glade" - -static void -gather_widgets (Application *app) -{ - typedef struct - { - void *location; - const char *name; - } WidgetInfo; - - const WidgetInfo widgets[] = - { - { &app->main_window, "main_window" }, - { &app->start_button, "start_button" }, - { &app->profile_button, "profile_button" }, - { &app->reset_button, "reset_button" }, - { &app->save_as_button, "save_as_button" }, - { &app->dummy_button, "dummy_button" }, - { &app->samples_label, "samples_label" }, - { &app->samples_hbox, "samples_hbox" }, - { &app->start_item, "start_item" }, - { &app->profile_item, "profile_item" }, - { &app->reset_item, "reset_item" }, - { &app->open_item, "open_item" }, - { &app->save_as_item, "save_as_item" }, - { &app->screenshot_item, "screenshot_item" }, - { &app->quit_item, "quit" }, - { &app->about_item, "about" }, - { &app->object_view, "object_view" }, - { &app->callers_view, "callers_view" }, - { &app->descendants_view, "descendants_view" }, - { &app->screenshot_window, "screenshot_window" }, - { &app->screenshot_textview, "screenshot_textview" }, - { &app->screenshot_close_button, "screenshot_close_button" }, - { &app->vpaned, "vpaned" }, - { &app->hpaned, "hpaned" }, - }; - - GladeXML *xml = glade_xml_new (GLADE_FILE, NULL, NULL); - int i; - - for (i = 0; i < G_N_ELEMENTS (widgets); ++i) - { - const WidgetInfo *info = &(widgets[i]); - - *(GtkWidget **)(info->location) = glade_xml_get_widget (xml, info->name); - - g_assert (GTK_IS_WIDGET (*(GtkWidget **)info->location)); - } - - g_object_unref (xml); -} - -static void -connect_signals (Application *app) -{ - typedef struct - { - gpointer object; - const char *signal; - gpointer callback; - gpointer data; - } SignalInfo; - - const SignalInfo signals[] = - { - { app->main_window, "delete_event", on_delete, NULL }, - { app->start_button, "toggled", on_start_toggled, app }, - { app->profile_button, "toggled", on_profile_toggled, app }, - { app->reset_button, "clicked", on_reset_clicked, app }, - { app->save_as_button, "clicked", on_save_as_clicked, app }, - { app->start_item, "activate", on_menu_item_activated, app->start_button }, - { app->profile_item, "activate", on_menu_item_activated, app->profile_button }, - { app->reset_item, "activate", on_reset_clicked, app }, - { app->open_item, "activate", on_open_clicked, app }, - { app->save_as_item, "activate", on_save_as_clicked, app }, - { app->screenshot_item, "activate", on_screenshot_activated, app }, - { app->quit_item, "activate", on_delete, NULL }, - { app->about_item, "activate", on_about_activated, app }, - { app->object_selection, "changed", on_object_selection_changed, app }, - { app->callers_view, "row-activated", on_callers_row_activated, app }, - { app->descendants_view, "row-activated", on_descendants_row_activated, app }, - { app->descendants_view, "row-expanded", on_descendants_row_expanded_or_collapsed, app }, - { app->descendants_view, "row-collapsed", on_descendants_row_expanded_or_collapsed, app }, - { app->screenshot_window, "delete_event", on_screenshot_window_delete, app }, - { app->screenshot_close_button, "clicked", on_screenshot_close_button_clicked, app }, - }; - - int i; - - for (i = 0; i < G_N_ELEMENTS (signals); ++i) - { - const SignalInfo *info = &(signals[i]); - - g_signal_connect (info->object, info->signal, info->callback, info->data); - } -} - -static void -set_shadows (void) -{ - /* Get rid of motif out-bevels */ - gtk_rc_parse_string ( - "style \"blah\" " - "{ " - " GtkToolbar::shadow_type = none " - " GtkMenuBar::shadow_type = none " - " GtkMenuBar::internal_padding = 2 " - "} " - "widget \"*toolbar\" style : rc \"blah\"\n" - "widget \"*menubar\" style : rc \"blah\"\n" - ); -} - -static void -set_icons (Application *app) -{ - const char *icon_files [] = { - PIXMAPDIR "/sysprof-icon-16.png", - PIXMAPDIR "/sysprof-icon-24.png", - PIXMAPDIR "/sysprof-icon-32.png", - PIXMAPDIR "/sysprof-icon-48.png", - PIXMAPDIR "/sysprof-icon-256.png", - NULL - }; - GList *pixbufs = NULL; - int i; - - for (i = 0; icon_files[i] != NULL; ++i) - { - const char *file = icon_files[i]; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (file, NULL); - - if (pixbuf) - { - pixbufs = g_list_prepend (pixbufs, pixbuf); - - if (i == 3) /* 48 x 48 */ - app->icon = g_object_ref (pixbuf); - } - else - { - g_warning ("Could not open %s\n", file); - } - } - - gtk_window_set_icon_list (GTK_WINDOW (app->main_window), pixbufs); - - g_list_foreach (pixbufs, (GFunc)g_object_unref, NULL); - g_list_free (pixbufs); -} - -#define PCT_FORMAT "%.2f %%" - -static gboolean -build_gui (Application *app) -{ - GtkTreeViewColumn *col; - - set_shadows (); - - if (!g_file_test (GLADE_FILE, G_FILE_TEST_EXISTS)) - { - sorry (NULL, - "Sysprof was not compiled or installed correctly.\n" - "\n" - "Running \"make install\" may solve this problem.\n"); - - return FALSE; - } - - gather_widgets (app); - - g_assert (app->main_window); - - /* Main Window */ - set_icons (app); - - gtk_widget_realize (GTK_WIDGET (app->main_window)); - - /* Tool items */ - gtk_toggle_tool_button_set_active ( - GTK_TOGGLE_TOOL_BUTTON (app->profile_button), FALSE); - - /* TreeViews */ - - /* object view */ - gtk_tree_view_set_enable_search (app->object_view, FALSE); - col = add_plain_text_column (app->object_view, _("Functions"), - OBJECT_NAME); - add_double_format_column (app->object_view, _("Self"), - OBJECT_SELF, PCT_FORMAT); - add_double_format_column (app->object_view, _("Total"), - OBJECT_TOTAL, PCT_FORMAT); - app->object_selection = gtk_tree_view_get_selection (app->object_view); - gtk_tree_view_column_set_expand (col, TRUE); - - /* callers view */ - gtk_tree_view_set_enable_search (app->callers_view, FALSE); - col = add_plain_text_column (app->callers_view, _("Callers"), - CALLERS_NAME); - add_double_format_column (app->callers_view, _("Self"), - CALLERS_SELF, PCT_FORMAT); - add_double_format_column (app->callers_view, _("Total"), - CALLERS_TOTAL, PCT_FORMAT); - gtk_tree_view_column_set_expand (col, TRUE); - - /* descendants view */ - gtk_tree_view_set_enable_search (app->descendants_view, FALSE); - col = add_plain_text_column (app->descendants_view, _("Descendants"), - DESCENDANTS_NAME); - add_double_format_column (app->descendants_view, _("Self"), - DESCENDANTS_SELF, PCT_FORMAT); - add_double_format_column (app->descendants_view, _("Cumulative"), - DESCENDANTS_CUMULATIVE, PCT_FORMAT); - gtk_tree_view_column_set_expand (col, TRUE); - - /* screenshot window */ - - /* set sizes */ - set_sizes (GTK_WINDOW (app->main_window), - GTK_WINDOW (app->screenshot_window), - app->hpaned, app->vpaned); - - /* hide/show widgets */ - gtk_widget_show_all (app->main_window); - gtk_widget_hide (app->dummy_button); - gtk_widget_hide (app->screenshot_window); - - gtk_widget_grab_focus (GTK_WIDGET (app->object_view)); - queue_show_samples (app); - - connect_signals (app); - - return TRUE; -} - -static void -on_new_sample (gboolean first_sample, - gpointer data) -{ - Application *app = data; - - if (app->state == PROFILING && first_sample) - update_sensitivity (app); - else - queue_show_samples (app); -} - -static Application * -application_new (void) -{ - Application *app = g_new0 (Application, 1); - - app->collector = collector_new (FALSE, on_new_sample, app); - app->state = INITIAL; - - return app; -} - -typedef struct -{ - const char *filename; - Application *app; -} FileOpenData; - -static gboolean -load_file (gpointer data) -{ - FileOpenData *file_open_data = data; - const char *filename = file_open_data->filename; - Application *app = file_open_data->app; - GError *err = NULL; - Profile *profile; - - set_busy (app->main_window, TRUE); - - profile = profile_load (filename, &err); - - if (profile) - { - set_loaded_profile (app, filename, profile); - - gdk_window_process_all_updates (); - set_busy (app->main_window, FALSE); - } - else - { - set_busy (app->main_window, FALSE); - - show_could_not_open (app, filename, err); - g_error_free (err); - } - - g_free (file_open_data); - - return FALSE; -} - -static const char * -process_options (int argc, - char **argv) -{ - int i; - gboolean show_version = FALSE; - const char *filename = NULL; - - for (i = 1; i < argc; ++i) - { - char *option = argv[i]; - - if (strcmp (option, "--version") == 0) - { - show_version = TRUE; - } - else if (!filename) - { - filename = argv[i]; - } - } - - if (show_version) - { - g_print ("%s %s\n", APPLICATION_NAME, PACKAGE_VERSION); - - exit (1); - } - - return filename; -} - -static void -apply_workarounds (void) -{ - - /* Disable gslice, since it - * - * - confuses valgrind - * - caches too much memory - * - hides memory access bugs - * - is not faster than malloc - * - * Note that g_slice_set_config() is broken in some versions of - * GLib (and 'declared internal' according to Tim), so we use the - * environment variable instead. - */ - if (!getenv ("G_SLICE")) - putenv ("G_SLICE=always_malloc"); - - /* Accessibility prevents sysprof from working reliably, so - * disable it. Specifically, it - * - * - causes large amounts of time to be spent in sysprof itself - * whenever the label is updated. - * - sometimes hangs at shutdown - * - does long-running roundtrip requests that prevents - * reading the event buffers, resulting in lost events. - */ - putenv ("NO_GAIL=1"); - putenv ("NO_AT_BRIDGE=1"); -} - -int -main (int argc, - char **argv) -{ - Application *app; - const char *filename; - - apply_workarounds(); - - filename = process_options (argc, argv); - - gtk_init (&argc, &argv); - - app = application_new (); - - if (!build_gui (app)) - return -1; - - update_sensitivity (app); - - if (filename) - { - FileOpenData *file_open_data = g_new0 (FileOpenData, 1); - file_open_data->filename = filename; - file_open_data->app = app; - - /* This has to run at G_PRIORITY_LOW because of bug 350517 - */ - g_idle_add_full (G_PRIORITY_LOW, load_file, file_open_data, NULL); - } - - gtk_main (); - - return 0; + return ret; } diff --git a/src/sysprof.glade b/src/sysprof.glade deleted file mode 100644 index dea0ade3..00000000 --- a/src/sysprof.glade +++ /dev/null @@ -1,926 +0,0 @@ - - - - - - - System Profiler - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 700 - 500 - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - True - False - 0 - - - - True - GTK_PACK_DIRECTION_LTR - GTK_PACK_DIRECTION_LTR - - - - True - _Profiler - True - - - - - - - True - _Start - True - - - - - True - gtk-media-play - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Profile - True - - - - - True - gtk-justify-left - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Reset - True - - - - - True - gtk-clear - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - - - - - - True - _Open... - True - - - - - - True - gtk-open - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - Save _As... - True - - - - - - True - gtk-save-as - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - - - - - - True - _Quit - True - - - - - - True - gtk-quit - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - - - - - True - _View - True - - - - - - - - True - _Screenshot Window - True - False - - - - - - - - - - - True - _Help - True - - - - - - - True - _About - True - - - - - True - gtk-about - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - - - - 0 - False - False - - - - - - True - 0.5 - 0.5 - 1 - 1 - 0 - 3 - 3 - 3 - - - - True - - - - - 0 - False - True - - - - - - True - False - 0 - - - - True - GTK_ORIENTATION_HORIZONTAL - True - True - - - - True - S_tart - True - gtk-media-play - True - True - True - False - - - False - True - - - - - - True - _Profile - True - gtk-justify-left - True - True - True - False - start_button - - - False - True - - - - - - True - _Reset - True - gtk-clear - True - True - False - - - False - True - - - - - - True - True - True - False - - - - True - True - True - True - - - - - False - False - - - - - - True - gtk-save-as - True - True - True - - - False - True - - - - - - True - dummy - True - True - True - False - False - start_button - - - False - True - - - - - 3 - True - True - - - - - - True - GTK_ORIENTATION_HORIZONTAL - GTK_TOOLBAR_BOTH_HORIZ - True - False - - - - True - True - True - False - - - - True - False - False - GTK_POS_TOP - False - False - - - - True - False - 0 - - - - True - Samples: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - False - False - GTK_POS_TOP - False - False - - - - True - 0 - False - False - GTK_JUSTIFY_LEFT - False - False - 1.0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - False - True - - - - - - True - label6 - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - True - 888888 - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - False - True - - - - - - True - label7 - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - 0 - True - True - - - - - False - True - - - - - - True - label2 - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - True - Samples: 8888888 - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - False - True - - - - - - True - label3 - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - - False - False - - - - - 3 - False - True - - - - - 0 - False - False - - - - - - True - False - 0 - - - - 3 - True - True - - - - True - True - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - True - False - False - False - - - - - True - False - - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - True - False - False - False - - - - - True - True - - - - - True - False - - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - True - False - False - False - - - - - True - True - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - - - Screenshot - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_UTILITY - GDK_GRAVITY_NORTH_WEST - True - False - - - - 12 - True - False - 6 - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - True - 0.5 - 0.5 - 1 - 1 - 3 - 0 - 12 - 0 - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - False - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_NONE - False - 0 - 0 - 0 - 0 - 0 - 0 - - - - - - - - - - - True - <b>Screenshot</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - True - True - - - - - - True - GTK_BUTTONBOX_END - 0 - - - - True - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - 0 - False - True - - - - - - - diff --git a/sysprof.doap b/sysprof.doap index 92d2a997..d989e5d9 100644 --- a/sysprof.doap +++ b/sysprof.doap @@ -18,6 +18,11 @@ + + Christian Hergert + + chergert + Søren Sandmann Pedersen diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..c37727a4 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,37 @@ +TESTS = + +test_cflags = \ + $(SYSPROF_CFLAGS) \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/lib + +test_libs = \ + $(SYSPROF_LIBS) \ + $(top_builddir)/lib/libsysprof-@API_VERSION@.la + + +TESTS += test-model-filter +test_model_filter_SOURCES = test-model-filter.c +test_model_filter_CFLAGS = $(test_cflags) +test_model_filter_LDADD = $(test_libs) + +TESTS += test-capture +test_capture_SOURCES = test-capture.c +test_capture_CFLAGS = $(test_cflags) +test_capture_LDADD = $(test_libs) + +TEST_ENVIRONMENT = \ + G_TEST_SRCDIR="$(abs_srcdir)" \ + G_TEST_BUILDDIR="$(abs_builddir)" \ + G_DEBUG=gc-friendly \ + GSETTINGS_BACKEND=memory \ + MALLOC_CHECK_=2 \ + MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) + +TEST_PROGS = test-model-filter +LOG_COMPILER = $(top_srcdir)/build-aux/tap-test + +noinst_PROGRAMS = $(TESTS) +check_PROGRAMS = $(TESTS) + +-include $(top_srcdir)/git.mk diff --git a/tests/test-capture.c b/tests/test-capture.c new file mode 100644 index 00000000..7ddb956c --- /dev/null +++ b/tests/test-capture.c @@ -0,0 +1,455 @@ +/* test-capture.c + * + * Copyright (C) 2016 Christian Hergert + * + * This file 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 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License + * along with this program. If not, see . + */ + +#include + +#include "sp-capture-reader.h" +#include "sp-capture-writer.h" + +static void +test_reader_basic (void) +{ + SpCaptureReader *reader; + SpCaptureWriter *writer; + GError *error = NULL; + gint64 t = g_get_monotonic_time (); + guint i; + gint r; + + writer = sp_capture_writer_new ("capture-file", 0); + g_assert (writer != NULL); + + reader = sp_capture_reader_new ("capture-file", &error); + g_assert_no_error (error); + g_assert (reader != NULL); + + for (i = 0; i < 100; i++) + { + gchar str[16]; + + g_snprintf (str, sizeof str, "%d", i); + + r = sp_capture_writer_add_map (writer, t, -1, -1, i, i + 1, i + 2, i + 3, str); + g_assert_cmpint (r, ==, TRUE); + } + + sp_capture_writer_flush (writer); + + for (i = 0; i < 100; i++) + { + SpCaptureFrameType type = -1; + const SpCaptureMap *map; + gchar str[16]; + + g_snprintf (str, sizeof str, "%d", i); + + if (!sp_capture_reader_peek_type (reader, &type)) + g_assert_not_reached (); + + g_assert_cmpint (type, ==, SP_CAPTURE_FRAME_MAP); + + map = sp_capture_reader_read_map (reader); + + g_assert (map != NULL); + g_assert_cmpint (map->frame.pid, ==, -1); + g_assert_cmpint (map->frame.cpu, ==, -1); + g_assert_cmpint (map->start, ==, i); + g_assert_cmpint (map->end, ==, i + 1); + g_assert_cmpint (map->offset, ==, i + 2); + g_assert_cmpint (map->inode, ==, i + 3); + g_assert_cmpstr (map->filename, ==, str); + } + + for (i = 0; i < 100; i++) + { + r = sp_capture_writer_add_timestamp (writer, t, i, -1); + g_assert_cmpint (r, ==, TRUE); + } + + sp_capture_writer_flush (writer); + + for (i = 0; i < 100; i++) + { + SpCaptureFrameType type = -1; + const SpCaptureTimestamp *ts; + + if (!sp_capture_reader_peek_type (reader, &type)) + g_assert_not_reached (); + + g_assert_cmpint (type, ==, SP_CAPTURE_FRAME_TIMESTAMP); + + ts = sp_capture_reader_read_timestamp (reader); + + g_assert (ts != NULL); + g_assert_cmpint (ts->frame.cpu, ==, i); + g_assert_cmpint (ts->frame.pid, ==, -1); + } + + for (i = 0; i < 100; i++) + { + r = sp_capture_writer_add_exit (writer, t, i, -1); + g_assert_cmpint (r, ==, TRUE); + } + + sp_capture_writer_flush (writer); + + for (i = 0; i < 100; i++) + { + SpCaptureFrameType type = -1; + const SpCaptureExit *ex; + + if (!sp_capture_reader_peek_type (reader, &type)) + g_assert_not_reached (); + + g_assert_cmpint (type, ==, SP_CAPTURE_FRAME_EXIT); + + ex = sp_capture_reader_read_exit (reader); + + g_assert (ex != NULL); + g_assert_cmpint (ex->frame.cpu, ==, i); + g_assert_cmpint (ex->frame.pid, ==, -1); + } + + for (i = 0; i < 100; i++) + { + char cmdline[32]; + + g_snprintf (cmdline, sizeof cmdline, "program-%d", i); + r = sp_capture_writer_add_process (writer, t, -1, i, cmdline); + g_assert_cmpint (r, ==, TRUE); + } + + sp_capture_writer_flush (writer); + + for (i = 0; i < 100; i++) + { + SpCaptureFrameType type = -1; + const SpCaptureProcess *pr; + char str[32]; + + g_snprintf (str, sizeof str, "program-%d", i); + + if (!sp_capture_reader_peek_type (reader, &type)) + g_assert_not_reached (); + + g_assert_cmpint (type, ==, SP_CAPTURE_FRAME_PROCESS); + + pr = sp_capture_reader_read_process (reader); + + g_assert (pr != NULL); + g_assert_cmpint (pr->frame.cpu, ==, -1); + g_assert_cmpint (pr->frame.pid, ==, i); + g_assert_cmpstr (pr->cmdline, ==, str); + } + + for (i = 0; i < 100; i++) + { + r = sp_capture_writer_add_fork (writer, t, i, -1, i); + g_assert_cmpint (r, ==, TRUE); + } + + sp_capture_writer_flush (writer); + + for (i = 0; i < 100; i++) + { + SpCaptureFrameType type = -1; + const SpCaptureFork *ex; + + if (!sp_capture_reader_peek_type (reader, &type)) + g_assert_not_reached (); + + g_assert_cmpint (type, ==, SP_CAPTURE_FRAME_FORK); + + ex = sp_capture_reader_read_fork (reader); + + g_assert (ex != NULL); + g_assert_cmpint (ex->frame.cpu, ==, i); + g_assert_cmpint (ex->frame.pid, ==, -1); + g_assert_cmpint (ex->child_pid, ==, i); + } + + for (i = 0; i < 1000; i++) + { + SpCaptureAddress addr; + gchar str[32]; + + g_snprintf (str, sizeof str, "jitstring-%d", i); + + addr = sp_capture_writer_add_jitmap (writer, str); + g_assert_cmpint (addr, ==, (i + 1) | SP_CAPTURE_JITMAP_MARK); + } + + sp_capture_writer_flush (writer); + + i = 0; + + for (;;) + { + SpCaptureFrameType type = -1; + g_autoptr(GHashTable) ret = NULL; + + if (sp_capture_reader_peek_type (reader, &type)) + g_assert_cmpint (type, ==, SP_CAPTURE_FRAME_JITMAP); + else + break; + + ret = sp_capture_reader_read_jitmap (reader); + g_assert (ret != NULL); + + i += g_hash_table_size (ret); + } + + g_assert_cmpint (1000, ==, i); + + { + SpCaptureFrameType type = -1; + + if (sp_capture_reader_peek_type (reader, &type)) + g_assert_not_reached (); + } + + for (i = 1; i <= 1000; i++) + { + SpCaptureAddress *addrs; + guint j; + + addrs = alloca (i * sizeof *addrs); + + for (j = 0; j < i; j++) + addrs[j] = i; + + if (!sp_capture_writer_add_sample (writer, t, -1, -1, addrs, i)) + g_assert_not_reached (); + } + + sp_capture_writer_flush (writer); + + for (i = 1; i <= 1000; i++) + { + SpCaptureFrameType type = -1; + const SpCaptureSample *sample; + guint j; + + if (!sp_capture_reader_peek_type (reader, &type)) + g_assert_not_reached (); + + g_assert_cmpint (type, ==, SP_CAPTURE_FRAME_SAMPLE); + + sample = sp_capture_reader_read_sample (reader); + + g_assert (sample != NULL); + g_assert_cmpint (sample->frame.time, ==, t); + g_assert_cmpint (sample->frame.cpu, ==, -1); + g_assert_cmpint (sample->frame.pid, ==, -1); + g_assert_cmpint (sample->n_addrs, ==, i); + + for (j = 0; j < i; j++) + g_assert_cmpint (sample->addrs[j], ==, i); + } + + { + SpCaptureFrameType type = -1; + + if (sp_capture_reader_peek_type (reader, &type)) + g_assert_not_reached (); + } + + r = sp_capture_writer_save_as (writer, "capture-file.bak", &error); + g_assert_no_error (error); + g_assert (r); + g_assert (g_file_test ("capture-file.bak", G_FILE_TEST_IS_REGULAR)); + + g_clear_pointer (&writer, sp_capture_writer_unref); + g_clear_pointer (&reader, sp_capture_reader_unref); + + reader = sp_capture_reader_new ("capture-file.bak", &error); + g_assert_no_error (error); + g_assert (reader != NULL); + + for (i = 0; i < 2; i++) + { + SpCaptureFrameType type = -1; + guint count = 0; + + while (sp_capture_reader_peek_type (reader, &type)) + { + count++; + if (!sp_capture_reader_skip (reader)) + g_assert_not_reached (); + } + + g_assert_cmpint (count, >, 1500); + + sp_capture_reader_reset (reader); + } + + sp_capture_reader_unref (reader); + + g_unlink ("capture-file"); + g_unlink ("capture-file.bak"); +} + +static void +test_writer_splice (void) +{ + SpCaptureWriter *writer1; + SpCaptureWriter *writer2; + SpCaptureReader *reader; + SpCaptureFrameType type; + GError *error = NULL; + guint i; + gint r; + + writer1 = sp_capture_writer_new ("writer1.syscap", 0); + writer2 = sp_capture_writer_new ("writer2.syscap", 0); + + for (i = 0; i < 1000; i++) + sp_capture_writer_add_timestamp (writer1, SP_CAPTURE_CURRENT_TIME, -1, -1); + + r = sp_capture_writer_splice (writer1, writer2, &error); + g_assert_no_error (error); + g_assert (r == TRUE); + + g_clear_pointer (&writer1, sp_capture_writer_unref); + g_clear_pointer (&writer2, sp_capture_writer_unref); + + reader = sp_capture_reader_new ("writer2.syscap", &error); + g_assert_no_error (error); + g_assert (reader != NULL); + + for (i = 0; i < 1000; i++) + { + const SpCaptureTimestamp *ts = sp_capture_reader_read_timestamp (reader); + + g_assert (ts != NULL); + g_assert_cmpint (ts->frame.cpu, ==, -1); + g_assert_cmpint (ts->frame.pid, ==, -1); + g_assert_cmpint (ts->frame.time, >, 0); + } + + r = sp_capture_reader_peek_type (reader, &type); + g_assert_cmpint (r, ==, FALSE); + + g_clear_pointer (&reader, sp_capture_reader_unref); + + g_unlink ("writer1.syscap"); + g_unlink ("writer2.syscap"); +} + +static void +test_reader_splice (void) +{ + SpCaptureWriter *writer1; + SpCaptureWriter *writer2; + SpCaptureReader *reader; + SpCaptureFrameType type; + GError *error = NULL; + guint i; + guint count; + gint r; + + writer1 = sp_capture_writer_new ("writer1.syscap", 0); + writer2 = sp_capture_writer_new ("writer2.syscap", 0); + + for (i = 0; i < 1000; i++) + sp_capture_writer_add_timestamp (writer1, SP_CAPTURE_CURRENT_TIME, -1, -1); + + r = sp_capture_writer_flush (writer1); + g_assert_cmpint (r, ==, TRUE); + + g_clear_pointer (&writer1, sp_capture_writer_unref); + + reader = sp_capture_reader_new ("writer1.syscap", &error); + g_assert_no_error (error); + g_assert (reader != NULL); + + /* advance to the end of the reader to non-start boundary for fd */ + + for (i = 0; i < 1000; i++) + { + const SpCaptureTimestamp *ts = sp_capture_reader_read_timestamp (reader); + + g_assert (ts != NULL); + g_assert_cmpint (ts->frame.cpu, ==, -1); + g_assert_cmpint (ts->frame.pid, ==, -1); + g_assert_cmpint (ts->frame.time, >, 0); + } + + r = sp_capture_reader_peek_type (reader, &type); + g_assert_cmpint (r, ==, FALSE); + + r = sp_capture_reader_splice (reader, writer2, &error); + g_assert_no_error (error); + g_assert_cmpint (r, ==, TRUE); + + g_clear_pointer (&reader, sp_capture_reader_unref); + g_clear_pointer (&writer2, sp_capture_writer_unref); + + reader = sp_capture_reader_new ("writer2.syscap", 0); + + for (i = 0; i < 1000; i++) + { + const SpCaptureTimestamp *ts = sp_capture_reader_read_timestamp (reader); + + g_assert (ts != NULL); + g_assert_cmpint (ts->frame.cpu, ==, -1); + g_assert_cmpint (ts->frame.pid, ==, -1); + g_assert_cmpint (ts->frame.time, >, 0); + } + + r = sp_capture_reader_peek_type (reader, &type); + g_assert_cmpint (r, ==, FALSE); + + g_clear_pointer (&reader, sp_capture_reader_unref); + + reader = sp_capture_reader_new ("writer2.syscap", &error); + g_assert_no_error (error); + g_assert (reader != NULL); + + r = sp_capture_reader_save_as (reader, "writer3.syscap", &error); + g_assert_no_error (error); + g_assert_cmpint (r, ==, TRUE); + + g_clear_pointer (&reader, sp_capture_reader_unref); + + reader = sp_capture_reader_new ("writer3.syscap", &error); + g_assert_no_error (error); + g_assert (reader != NULL); + + count = 0; + while (sp_capture_reader_skip (reader)) + count++; + g_assert_cmpint (count, ==, 1000); + + g_clear_pointer (&reader, sp_capture_reader_unref); + + g_unlink ("writer1.syscap"); + g_unlink ("writer2.syscap"); + g_unlink ("writer3.syscap"); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/SpCapture/ReaderWriter", test_reader_basic); + g_test_add_func ("/SpCapture/Writer/splice", test_writer_splice); + g_test_add_func ("/SpCapture/Reader/splice", test_reader_splice); + return g_test_run (); +} diff --git a/tests/test-model-filter.c b/tests/test-model-filter.c new file mode 100644 index 00000000..89230840 --- /dev/null +++ b/tests/test-model-filter.c @@ -0,0 +1,119 @@ +#include + +#include "sp-model-filter.h" + +#define TEST_TYPE_ITEM (test_item_get_type()) + +struct _TestItem +{ + GObject p; + guint n; +}; + +G_DECLARE_FINAL_TYPE (TestItem, test_item, TEST, ITEM, GObject) +G_DEFINE_TYPE (TestItem, test_item, G_TYPE_OBJECT) + +static void +test_item_class_init (TestItemClass *klass) +{ +} + +static void +test_item_init (TestItem *self) +{ +} + +static TestItem * +test_item_new (guint n) +{ + TestItem *item; + + item = g_object_new (TEST_TYPE_ITEM, NULL); + item->n = n; + + return item; +} + +static gboolean +filter_func1 (GObject *object, + gpointer user_data) +{ + return (TEST_ITEM (object)->n & 1) == 0; +} + +static gboolean +filter_func2 (GObject *object, + gpointer user_data) +{ + return (TEST_ITEM (object)->n & 1) == 1; +} + +static void +test_basic (void) +{ + GListStore *model; + SpModelFilter *filter; + TestItem *item; + guint i; + + model = g_list_store_new (TEST_TYPE_ITEM); + g_assert (model); + + for (i = 0; i < 1000; i++) + { + g_autoptr(TestItem) val = test_item_new (i); + + g_list_store_append (model, val); + } + + g_assert_cmpint (1000, ==, g_list_model_get_n_items (G_LIST_MODEL (model))); + + filter = sp_model_filter_new (G_LIST_MODEL (model)); + g_assert (filter); + + g_assert_cmpint (1000, ==, g_list_model_get_n_items (G_LIST_MODEL (filter))); + sp_model_filter_set_filter_func (filter, filter_func1, NULL, NULL); + g_assert_cmpint (500, ==, g_list_model_get_n_items (G_LIST_MODEL (filter))); + + for (i = 0; i < 1000; i += 2) + g_list_store_remove (model, 998 - i); + + g_assert_cmpint (500, ==, g_list_model_get_n_items (G_LIST_MODEL (model))); + g_assert_cmpint (0, ==, g_list_model_get_n_items (G_LIST_MODEL (filter))); + + sp_model_filter_set_filter_func (filter, NULL, NULL, NULL); + g_assert_cmpint (500, ==, g_list_model_get_n_items (G_LIST_MODEL (filter))); + + sp_model_filter_set_filter_func (filter, filter_func2, NULL, NULL); + g_assert_cmpint (500, ==, g_list_model_get_n_items (G_LIST_MODEL (filter))); + + g_list_store_append (model, test_item_new (1001)); + + for (i = 0; i < 500; i++) + g_list_store_remove (model, 0); + + g_assert_cmpint (1, ==, g_list_model_get_n_items (G_LIST_MODEL (model))); + g_assert_cmpint (1, ==, g_list_model_get_n_items (G_LIST_MODEL (filter))); + + sp_model_filter_set_filter_func (filter, NULL, NULL, NULL); + g_assert_cmpint (1, ==, g_list_model_get_n_items (G_LIST_MODEL (model))); + g_assert_cmpint (1, ==, g_list_model_get_n_items (G_LIST_MODEL (filter))); + + item = g_list_model_get_item (G_LIST_MODEL (filter), 0); + g_assert (item); + g_assert (TEST_IS_ITEM (item)); + g_assert_cmpint (item->n, ==, 1001); + g_clear_object (&item); + + g_clear_object (&model); + g_clear_object (&filter); +} + +gint +main (gint argc, + gchar *argv[]) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/SpModelFilter/basic", test_basic); + return g_test_run (); +} diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 00000000..0b1522aa --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,19 @@ +bin_PROGRAMS = sysprof-cli +noinst_PROGRAMS = sysprof-dump + +AM_CPPFLAGS = \ + $(SYSPROF_CFLAGS) \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/lib + +sysprof_cli_SOURCES = sysprof-cli.c +sysprof_cli_LDADD = \ + $(SYSPROF_LIBS) \ + $(top_builddir)/lib/libsysprof-@API_VERSION@.la + +sysprof_dump_SOURCES = sysprof-dump.c +sysprof_dump_LDADD = \ + $(SYSPROF_LIBS) \ + $(top_builddir)/lib/libsysprof-@API_VERSION@.la + +-include $(top_srcdir)/git.mk diff --git a/tools/sysprof-cli.c b/tools/sysprof-cli.c index e8d26ee2..f94ca414 100644 --- a/tools/sysprof-cli.c +++ b/tools/sysprof-cli.c @@ -1,10 +1,10 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2005, Lorenzo Colitti - * Copyright 2005, Soren Sandmann +/* sysprof-cli.c * - * This program is free software; you can redistribute it and/or modify + * Copyright (C) 2016 Christian Hergert + * + * 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 + * 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, @@ -13,158 +13,208 @@ * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program. If not, see . */ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#include -#include -#include #include +#include +#include #include -#include -#include +#include +#include +#include +#include +#include -#include "stackstash.h" -#include "profile.h" -#include "watch.h" -#include "signal-handler.h" -#include "collector.h" - -typedef struct Application Application; -struct Application -{ - Collector * collector; - char * outfile; - GMainLoop * main_loop; -}; +static GMainLoop *main_loop; +static SpProfiler *profiler; +static int efd = -1; +static int exit_code = EXIT_SUCCESS; static void -dump_data (Application *app) +signal_handler (int signum) { - GError *err = NULL; - Profile *profile; + gint64 val = 1; - printf ("Saving profile (%d samples) in %s ... ", - collector_get_n_samples (app->collector), - app->outfile); - fflush (stdout); - - collector_stop (app->collector); - - profile = collector_create_profile (app->collector); - profile_save (profile, app->outfile, &err); - - if (err) - { - printf ("failed\n"); - fprintf (stderr, "Error saving %s: %s\n", app->outfile, err->message); - exit (1); - } - else - { - printf ("done\n\n"); - } -} - -static void -signal_handler (int signo, - gpointer data) -{ - Application *app = data; - - dump_data (app); - - while (g_main_context_iteration (NULL, FALSE)) - ; - - g_main_loop_quit (app->main_loop); -} - -static char * -usage_msg (const char *name) -{ - return g_strdup_printf ( - "Usage: \n" - " %s \n" - "\n" - "On SIGTERM or SIGINT (Ctrl-C) the profile will be written to ", - name); + write (efd, &val, sizeof val); } static gboolean -file_exists_and_is_dir (const char *name) +dispatch (GSource *source, + GSourceFunc callback, + gpointer callback_data) { - return - g_file_test (name, G_FILE_TEST_EXISTS) && - g_file_test (name, G_FILE_TEST_IS_DIR); + sp_profiler_stop (profiler); + g_main_loop_quit (main_loop); + return G_SOURCE_REMOVE; +} + +static GSourceFuncs source_funcs = { + NULL, NULL, dispatch, NULL +}; + +static void +profiler_stopped (SpProfiler *profiler, + GMainLoop *main_loop) +{ + g_main_loop_quit (main_loop); } static void -die (const char *err_msg) +profiler_failed (SpProfiler *profiler, + const GError *reason, + GMainLoop *main_loop) { - if (err_msg) - fprintf (stderr, "\n%s\n\n", err_msg); - - exit (-1); + g_assert (SP_IS_PROFILER (profiler)); + g_assert (reason != NULL); + + g_printerr ("Failure: %s\n", reason->message); + exit_code = EXIT_FAILURE; } -static int opt_pid = -1; - -static GOptionEntry entries[] = +gint +main (gint argc, + gchar *argv[]) { - { "pid", 'p', 0, G_OPTION_ARG_INT, &opt_pid, - "Make sysprof specific to a task", NULL }, - { NULL } -}; + SpCaptureWriter *writer; + SpSource *source; + GMainContext *main_context; + GOptionContext *context; + const gchar *filename = "capture.syscap"; + GError *error = NULL; + GSource *gsource; + gchar *command = NULL; + int pid = -1; + int fd; + GOptionEntry entries[] = { + { "pid", 'p', 0, G_OPTION_ARG_INT, &pid, N_("Make sysprof specific to a task"), N_("PID") }, + { "command", 'c', 0, G_OPTION_ARG_STRING, &command, N_("Run a command and profile the process"), N_("COMMAND") }, + { NULL } + }; -int -main (int argc, char *argv[]) -{ - Application *app; - GOptionContext *context; - GError *err; - - err = NULL; + sp_clock_init (); - context = g_option_context_new ("- Sysprof"); - g_option_context_add_main_entries (context, entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &err)) + context = g_option_context_new (_("[CAPTURE_FILE] - Sysprof")); + g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + + if (!g_option_context_parse (context, &argc, &argv, &error)) { - g_print ("Failed to parse options: %s\n", err->message); - return 1; + g_printerr ("%s\n", error->message); + return EXIT_FAILURE; } - app = g_new0 (Application, 1); - app->collector = collector_new (FALSE, NULL, NULL); - app->outfile = g_strdup (argv[1]); - app->main_loop = g_main_loop_new (NULL, 0); - - if (!collector_start (app->collector, (pid_t) opt_pid, &err)) - die (err->message); - - if (argc < 2) - die (usage_msg (argv[0])); - - if (!signal_set_handler (SIGTERM, signal_handler, app, &err)) - die (err->message); - - if (!signal_set_handler (SIGINT, signal_handler, app, &err)) - die (err->message); - - if (file_exists_and_is_dir (app->outfile)) + if (argc > 2) { - char *msg = g_strdup_printf ("Can't write to %s: is a directory\n", - app->outfile); - die (msg); + gint i; + + g_printerr (_("Too many arguments were passed to sysprof-cli:")); + + for (i = 2; i < argc; i++) + g_printerr (" %s", argv [i]); + g_printerr ("\n"); + + return EXIT_FAILURE; } - - g_main_loop_run (app->main_loop); - - signal_unset_handler (SIGTERM); - signal_unset_handler (SIGINT); - - return 0; + + efd = eventfd (0, O_CLOEXEC); + + main_loop = g_main_loop_new (NULL, FALSE); + + gsource = g_source_new (&source_funcs, sizeof (GSource)); + g_source_add_unix_fd (gsource, efd, G_IO_IN); + g_source_attach (gsource, NULL); + + profiler = sp_profiler_new (); + + g_signal_connect (profiler, + "failed", + G_CALLBACK (profiler_failed), + main_loop); + + g_signal_connect (profiler, + "stopped", + G_CALLBACK (profiler_stopped), + main_loop); + + if (argc == 2) + filename = argv[1]; + + if (-1 == (fd = g_open (filename, O_CREAT | O_RDWR | O_CLOEXEC, 0640))) + { + g_printerr ("Failed to open %s\n", filename); + return EXIT_FAILURE; + } + + if (command != NULL) + { + g_auto(GStrv) child_argv = NULL; + gint child_argc; + + if (!g_shell_parse_argv (command, &child_argc, &child_argv, &error)) + { + g_printerr ("Invalid command: %s\n", error->message); + return EXIT_FAILURE; + } + + if (!g_spawn_async (NULL, child_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, &error)) + { + g_printerr ("Invalid command: %s\n", error->message); + return EXIT_FAILURE; + } + + /* Stop the process until we are setup */ + if (0 != kill (pid, SIGSTOP)) + { + g_printerr ("Failed to pause inferior during setup\n"); + return EXIT_FAILURE; + } + } + + writer = sp_capture_writer_new_from_fd (fd, 0); + sp_profiler_set_writer (profiler, writer); + + source = sp_proc_source_new (); + sp_profiler_add_source (profiler, source); + g_object_unref (source); + + source = sp_perf_source_new (); + + sp_profiler_add_source (profiler, source); + g_object_unref (source); + + if (pid != -1) + { + sp_profiler_set_whole_system (profiler, FALSE); + sp_profiler_add_pid (profiler, pid); + } + + signal (SIGINT, signal_handler); + signal (SIGTERM, signal_handler); + + sp_profiler_start (profiler); + + /* Restore the process if we stopped it */ + if (command) + kill (pid, SIGCONT); + + g_printerr ("Recording, press ^C to exit\n"); + + g_main_loop_run (main_loop); + + main_context = g_main_loop_get_context (main_loop); + while (g_main_context_pending (main_context)) + g_main_context_iteration (main_context, FALSE); + + close (efd); + + g_clear_pointer (&writer, sp_capture_writer_unref); + g_clear_object (&profiler); + g_clear_pointer (&main_loop, g_main_loop_unref); + g_clear_pointer (&context, g_option_context_free); + + return EXIT_SUCCESS; } diff --git a/tools/sysprof-dump.c b/tools/sysprof-dump.c new file mode 100644 index 00000000..3e7b46d9 --- /dev/null +++ b/tools/sysprof-dump.c @@ -0,0 +1,153 @@ +/* sysprof-dump.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 + +#include "sp-capture-reader.h" + +gint +main (gint argc, + gchar *argv[]) +{ + SpCaptureReader *reader; + SpCaptureFrameType type; + GError *error = NULL; + + if (argc != 2) + { + g_printerr ("usage: %s FILENAME\n", argv[0]); + return EXIT_FAILURE; + } + + reader = sp_capture_reader_new (argv[1], &error); + + if (reader == NULL) + { + g_printerr ("%s\n", error->message); + return EXIT_FAILURE; + } + + while (sp_capture_reader_peek_type (reader, &type)) + { + switch (type) + { + case SP_CAPTURE_FRAME_EXIT: + { + const SpCaptureExit *ex = sp_capture_reader_read_exit (reader); + + if (ex == NULL) + return EXIT_FAILURE; + + g_print ("EXIT: pid=%d\n", ex->frame.pid); + + break; + } + + case SP_CAPTURE_FRAME_FORK: + { + const SpCaptureFork *fk = sp_capture_reader_read_fork (reader); + + if (fk == NULL) + return EXIT_FAILURE; + + g_print ("FORK: pid=%d child_pid=%d\n", fk->frame.pid, fk->child_pid); + + break; + } + + case SP_CAPTURE_FRAME_JITMAP: + { + g_autoptr(GHashTable) ret = sp_capture_reader_read_jitmap (reader); + GHashTableIter iter; + SpCaptureAddress addr; + const gchar *str; + + if (ret == NULL) + return EXIT_FAILURE; + + g_print ("JITMAP:\n"); + + g_hash_table_iter_init (&iter, ret); + while (g_hash_table_iter_next (&iter, (gpointer *)&addr, (gpointer *)&str)) + g_print (" "SP_CAPTURE_ADDRESS_FORMAT" : %s\n", addr, str); + + break; + } + + case SP_CAPTURE_FRAME_MAP: + { + const SpCaptureMap *map = sp_capture_reader_read_map (reader); + + g_print ("MAP: pid=%d\n" + " start = %"G_GUINT64_FORMAT"\n" + " end = %"G_GUINT64_FORMAT"\n" + " offset = %"G_GUINT64_FORMAT"\n" + " inode = %"G_GUINT64_FORMAT"\n" + " filename = %s\n", + map->frame.pid, + map->start, map->end, map->offset, map->inode, map->filename); + + break; + } + + case SP_CAPTURE_FRAME_PROCESS: + { + const SpCaptureProcess *pr = sp_capture_reader_read_process (reader); + + if (pr == NULL) + perror ("Failed to read process"); + + g_print ("PROCESS: pid=%d cmdline=%s\n", pr->frame.pid, pr->cmdline); + break; + } + + case SP_CAPTURE_FRAME_SAMPLE: + { + const SpCaptureSample *s = sp_capture_reader_read_sample (reader); + guint i; + + g_print ("SAMPLE: pid=%d\n", s->frame.pid); + + for (i = 0; i < s->n_addrs; i++) + g_print (" "SP_CAPTURE_ADDRESS_FORMAT"\n", s->addrs[i]); + + break; + } + + case SP_CAPTURE_FRAME_TIMESTAMP: + { + const SpCaptureTimestamp *ts = sp_capture_reader_read_timestamp (reader); + g_print ("TIMESTAMP: pid=%d time=%"G_GINT64_FORMAT"\n", ts->frame.pid, ts->frame.time); + break; + } + + default: + g_print ("Skipping unknown frame type: (%d): ", type); + if (!sp_capture_reader_skip (reader)) + { + g_print ("Failed\n"); + return EXIT_FAILURE; + } + g_print ("Success\n"); + break; + } + } + + return EXIT_SUCCESS; +}